转载

java多线程并发编程(三) volatile关键字

一、volatile的作用

关键字volatile是使变量在多个线程间可见,也就是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

通过使用volatile关键字,强制的从公共内存中读取变量的值,内存结构如图所示:

java多线程并发编程(三) volatile关键字

使用volatile关键字增加了实例变量在多个线程之间的可见性。 但volatile关键字最致命的缺点是不支持原子性。

二、volatile非原子的特性

关键字volatile虽然增加了实例变量在多个线程之间的可见性,但它却不具备同步性,那么也就不具备原子性。

变量在内存中工作的过程如图

java多线程并发编程(三) volatile关键字

1、read和load阶段: 从主存复制变量到当前线程工作内存;

2、use和assign阶段: 执行代码,改变共享变量值;

3、store和write阶段: 用工作内存数据刷新主存对应变量的值。

在多线程环境中,use和assign是多次出现的,但这一操作并不是原子性,也就是在read和load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公共内存中的变量不同步,所以计算出来的结果会和预期不一样,也就出现了非线程安全问题。

对于用volatile修饰的变量,JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的,例如线程1和线程2在进行read和load的操作中,发现主内存中count的值都是5,那么都会加载这个最新的值。 也就是说,volatile关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量还是需要加锁同步。

三、使用原子类进行i++操作

除了在i++操作时使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现。

原子操作是不能分割的整体,没有其他线程能够中断或检查正在原子操作中的变量。 一个原子(atomic)类型就是一个原子操作可用的类型,它可以在没有锁的情况下做到线程安全(thread-safe)。

四、原子类也并不完全安全

原子类在具有有逻辑性的情况下输出结果也具有随机性。 要做到线程安全,还要保证调用原子类操作方法的同步

五、下面将关键字synchronized和volatile进行一下比较:

1、关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。 随着JDK新版本的发布,synchronized关键字在执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。

2、多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。

3、volatile能保证数据的可见性,但不能保证原子性; 而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。

4、再次重申一下,关键字volatile解决的是变量在多个线程之间的可见性; 而synchronized关键字解决的是多个线程之间访问资源的同步性。

5、线程安全包含原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程安全的。

六、synchronized代码块有volatile同步的功能

关键字synchronized可以使多个线程访问同一个资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能。

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。 它包含两个特征: 互斥性和可见性。 同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

学习多线程并发,要着重“外练互斥,内修可见”,这是掌握多线程、学习多线程并发的重要技术点。

原文  http://mp.weixin.qq.com/s?__biz=Mzg5OTE1NzQxNQ==&mid=2247483716&idx=1&sn=4339fa6a612edb32250150c9178277e1
正文到此结束
Loading...