博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程——线程安全
阅读量:2491 次
发布时间:2019-05-11

本文共 4058 字,大约阅读时间需要 13 分钟。

文章目录

前言

  小咸儿在上一篇多线程——线程通讯中,提到线程安全问题,今天就来说一说。


叙述

宝图

  先来一张导图来看看线程安全的分布?

在这里插入图片描述

线程安全

  线程安全是什么呢?

  当多个线程共享同一个全局变量,做写操作时,可能会收到其他线程的干扰,做读操作则不会发生线程安全。

解决方法

  既然遇到了线程安全问题,那么该如何解决这个问题呢?

这时候就会先考虑到两个关键字:volatilesynchronized

volatile关键字: 保证可见性的问题。保证另一个线程可见,及时将修改后的变量刷新到主内存中。虽然能够保证可见性,但是无法保证原子性。

代码展示:

package com.practice.demo.thread.lock;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLock {
private volatile Map
caChe = new HashMap<>(); /** * 读写锁 */ private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); /** * 写入锁 */ private ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock(); private ReentrantReadWriteLock.ReadLock readLock = rwl.readLock(); /** * 写入元素 * @param key key值 * @param value value值 */ public void put(String key, String value){
try {
writeLock.lock(); System.out.println("写入put方法key:" + key + ",value:" + value + ".开始"); Thread.currentThread().sleep(100); caChe.put(key,value); System.out.println("写入put方法key:" + key + ",value:" + value + ".结束"); } catch (Exception e){
}finally {
writeLock.unlock(); } } /** * 读取元素 * @param key key值 * */ public String get(String key){
try {
readLock.lock(); System.out.println("读取key:" + key + ".开始"); Thread.currentThread().sleep(100); String value = caChe.get(key); System.out.println("读取key:" + key + ".结束"); return value; }catch (Exception e){
return null; }finally {
readLock.unlock(); } } public static void main(String[] args){
ReadWriteLock readWriteLock = new ReadWriteLock(); // 写线程 Thread t1 = new Thread(new Runnable() {
@Override public void run() {
for (int i = 0; i < 10; i++){
readWriteLock.put("i", i + ""); } } }); // 读线程 Thread t2 = new Thread(new Runnable() {
@Override public void run() {
for (int i = 0; i < 10; i++){
readWriteLock.get(i + ""); } } }); t1.start(); t2.start(); }}

打印结果:

在这里插入图片描述

  结果: 即当这个线程开始和结束都执行完之后,另一个线程才能进行,并且全局变量发生变化后对另一个线程可见。

synchronized关键字: 内置锁,保证线程原子性,当线程进入方法的时候,自动获取锁,一旦锁被某个线程获取到后,其他的线程就会等待。即可修饰代码块也可用来修饰方法。

代码展示

public void run() {
// 使用account作为同步监视器,任何线程进入下面同步代码块之前 // 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改她 // 这种做法符合:“加锁——修改——释放锁”的逻辑 synchronized (account) {
// 账户余额大于取钱数目 if (account.getBalance() >= drawAmount) {
// 吐出钞票 System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount); try {
Thread.sleep(1); } catch (InterruptedException ex) {
ex.printStackTrace(); } // 修改余额 account.setBalance(account.getBalance() - drawAmount); System.out.println("\t余额为:" + account.getBalance()); } else {
System.out.println(getName() + "取钱失败,余额不足!"); } } }

区别

  volatile和synchronized的区别:

  • volatile不会进行加锁操作:

      volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会执行线程阻塞,因此volatile变量是一种比synchronized关键字更加轻量级的同步机制。

  • volatile不如synchronized安全:

      在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更加难以理解。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它。一般来说,使用同步机制会更安全。

  • volatile变量作用类似于同步变量读写操作:

      从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。

  • volatile无法同时保证内存可见性和原子性:

      加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性,原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count++”,“count = count + 1”。

问题

  解决线程安全的问题不仅只有这两种处理办法,还有一种锁(Lock),具体且听小咸儿下回分享。


总结

  多线程是十分实用并且常用的内容,接下来小咸儿还会继续深入学习多线程,更多的内容等待更新。

感谢您的阅读~~

转载地址:http://ajerb.baihongyu.com/

你可能感兴趣的文章
block使用小结、在arc中使用block、如何防止循环引用
查看>>
iPhone开发学习笔记002——Xib设计UITableViewCell然后动态加载
查看>>
iOS开发中遇到的问题整理 (一)
查看>>
Swift code into Object-C 出现 ***-swift have not found this file 的问题
查看>>
为什么你的App介绍写得像一坨翔?
查看>>
RTImageAssets插件--@3x可自动生成@2x图片
查看>>
iOS开发的一些奇巧淫技
查看>>
常浏览的博客和网站
查看>>
Xcode 工程文件打开不出来, cannot be opened because the project file cannot be parsed.
查看>>
iOS在Xcode6中怎么创建OC category文件
查看>>
5、JavaWeb学习之基础篇—标签(自定义&JSTL)
查看>>
8、JavaWEB学习之基础篇—文件上传&下载
查看>>
reRender属性的使用
查看>>
href="javascript:void(0)"
查看>>
h:panelGrid、h:panelGroup标签学习
查看>>
f:facet标签 的用法
查看>>
<h:panelgroup>相当于span元素
查看>>
java中append()的方法
查看>>
必学高级SQL语句
查看>>
经典SQL语句大全
查看>>