1、volatile与synchronized的异同点
1)volatile与synchronized都是java的关键字,且都具有内存可见性
2)synchronized具有内存可见性和原子性,不能禁止指令重排,但是通过as-if-serial语义(所有硬件的优化都必须遵守as-if-serial语义)可以保证程序执行的有序性。
3)volatile仅用于修饰变量,synchronized可以修饰变量、方法、代码块
2、volatile主要功能是什么、实现原理是什么
volatile是happen-before八大原则中的一条,在先行原则中定义,如果先写volatile修饰的变量,再读volatile修饰的变量,那么读操作一定在写操作之后。它所具有的功能是禁止指令重排以及保证内存可见性。
volatile之所以能实现这两个功能,主要是依赖内存屏障来实现(反编译class文件可以看到,内存屏障本质是一组cpu指令)。这里禁止指令重排好理解,因为编译器和处理器为了能更快的运行代码,所以它们会在一定程度上重新排列程序执行的顺序,当我们加入内存屏障后,就可以避免编译器处理器对屏障内的指令执行顺序做出修改。内存可见性这个要想理解,首先要知道工作内存和主内存的概念(不知道的话可以浏览该系列第一篇文章),当我们对工作内存中的变量做出修改后,volatile修改的变量会立刻刷新到主内存,从而被其它共用该变量的线程感知到。
3、synchronized主要功能是什么,其是如何实现的
synchronized具有锁的功能,可以保证内存可见性原子性,注意这里有一个指令重排的误解,那就是误以为该关键字可以禁止指令重排,其实该关键字不具备禁止指令重排的功能。但它可以通过as-if-serial语义保证程序执行的有序性。
synchronized实现锁功能时,主要是通过锁定对象来实现的。这个锁定的对象和new的对象其实是有区别的。我们new的对象的对象头分为mark word和Kclass两部分,mark word包含了对象的hashcode还有锁定状态等信息,实际锁定的就是这个mark word部分。
另外通过反编译后的字节码可以看到synchronized功能的实现主要有两种方式:
1)同步代码块采用monitorenter、monitorexit指令显式的实现。
1)同步方法则使用ACC_SYNCHRONIZED标记符隐式的实现
4、synchronized优化过程
jdk1.6之前synchronized是一个重量级的锁,使用较少,之后被优化后,在不同场景下有不同的锁选择。下面是锁大致的优化过程:
无锁:当只有一个线程时是无锁状态,此时通过CAS乐观锁实现资源竞争获取
偏向锁:一个资源总被同一线程访问,那么对象头会记录该线程ID,竞争获取锁时直接判断该线程是否与存储的线程ID相同
轻量级锁:偏向锁状态下,又有一个线程竞争资源,如果正好上一个线程释放资源,此时仍是偏向锁,如果没释放资源,则新来线程自旋等待,偏向锁升级为轻量级锁
重量级锁:轻量级锁状态下如果又有第三个竞争线程访问、或者轻量级锁状态下自旋超过一定次数升级为重量级锁