回顾一下两个关键字:synchronized和volatile
- Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized、volatile、final、concurren包等。
- synchronized通过加锁的方式,使得其在需要原子性、可见性和有序性这三种特性的时候都可以作为其中一种解决方案,看起来是“万能”的。的确,大部分并发控制操作都能使用synchronized来完成。
- volatile通过在volatile变量的操作前后插入内存屏障的方式,保证了变量在并发场景下的可见性和有序性。
- volatile关键字是无法保证原子性的,而synchronized通过monitorenter和monitorexit两个指令,可以保证被synchronized修饰的代码在同一时间只能被一个线程访问,即可保证不会出现CPU时间片在多个线程间切换,即可保证原子性。
那么,我们知道,synchronized和volatile两个关键字是Java并发编程中经常用到的两个关键字,而且,通过前面的回顾,我们知道synchronized可以保证并发编程中不会出现原子性、可见性和有序性问题,而volatile只能保证可见性和有序性,那么,既生synchronized、何生volatile?
接下来,本文就来论述一下,为什么Java中已经有了synchronized关键字,还要提供volatile关键字。
Synchronized的问题
我们都知道synchronized其实是一种加锁机制,那么既然是锁,天然就具备以下几个缺点:
1、有性能损耗
虽然在JDK 1.6中对synchronized做了很多优化,如如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等,但是他毕竟还是一种锁。
以上这几种优化,都是尽量想办法避免对Monitor进行加锁,但是,并不是所有情况都可以优化的,况且就算是经过优化,优化的过程也是有一定的耗时的。
所以,无论是使用同步方法还是同步代码块,在同步操作之前还是要进行加锁,同步操作之后需要进行解锁,这个加锁、解锁的过程是要有性能损耗的。
关于二者的性能对比,由于虚拟机对锁实行的许多消除和优化,使得我们很难量化这两者之间的性能差距,但是我们可以确定的一个基本原则是:volatile变量的读操作的性能小号普通变量几乎无差别,但是写操作由于需要插入内存屏障所以会慢一些,即便如此,volatile在大多数场景下也比锁的开销要低。
2、产生阻塞
关于synchronize的实现原理,无论是同步方法还是同步代码块,无论是ACC_SYNCHRONIZED还是monito