一 .CAS
在学习java.util.concurrent(简称JUC)包下的类时,了解到了CAS这个概念,整个JUC包的基础也是CAS,ReentrantLock也是基于它的。学习CAS,先从synchronized关键字说起,synchronized关键字能保证最基本的互斥同步。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。而互斥是实现同步的一种手段。互斥同步4个字,互斥是因,同步是果;互斥是方法,同步是目的。互斥同步属于一种悲观的并发政策,认为如果不进行正确的同步措施,那就好出现问题,无论共享数据是否真会出现竞争,它都要进行加锁。
随着硬件指令集的发展,多了一个选择:基于冲突检测的乐观并发政策,先进性操作,如果没有其他线程共享数据,则操作成功;如果共享数据有争用,产生冲突,那就采取其他措施(不断重试),这种乐观的并发政策的许多实现不用把线程挂起。乐观并发政策需要操作和冲突检测两个步骤具有原子性,需要底层硬件完成。
x86中通过cmpxchg汇编指令来完成CAS操作。CAS(Compare-and-Swap 比较和交换)与平台相关,它有三个操作数,内存位置值(V),旧的预期值(A),新值(B)。CAS指令执行时,当V=A时,处理器用B的值跟新V的值,否则不执行。上述的过程是一个原子操作。JDK1.5引入的CAS,它在sun.misc.Unsafe类里面方法提供。里面调用了Native方法。
下面给一个JUC包下面的Atomic类的部分源代码,执行自增操作。用到CAS,里面用到了循环一直判断。里面没有进行加锁处理。但是也有逻辑漏洞,在111和222如果其他线程被执行,获得V(V运来是A),将他修改为B,后来又修改会A,则执行222代码的时候认为V没有改变过,这就是“ABA”问题。这个问题一般没有什么影响。
[java]
view plain
copy
- //该方法实现了i++的非阻塞的原子操作
- public final int getAndIncrement() {
- for (;;) { //循环,使用CAS的经典方式,这是实现non-blocking方式的代价
- int current = get();//得到现在的值 111
- int next = current + 1;//通过计算得到要赋予的新值
- if (compareAndSet(current, next)) //关键点,调用CAS原子更新, 222
- return current;
- }
- }
二.锁优化
JDK1.6中高校并发是一个重要改进。里面的给出的各种锁都是为了线程间更高效的共享数据。优化的方法有下面几种。这里的锁优化主要是针对synchronized关键字来说,它产生的是一种重量级的锁定(重量级的锁定不是用CAS),会有互斥,效率较低。而在JDK1.6后,引入的自旋锁,轻量级锁,偏向锁对互斥同步进行了优化,它们三种锁默认都是开启的。
1.锁消除,锁粗化。锁消除是判断当堆上的数据不会被其他线程访问到时,该线程上的同步加锁就无需进行。
由于加锁和解锁的开销很大,如果不断的加锁和解锁操作都是对于同一个对象,虚拟机会把整个加锁同步的范围扩张到操作序列的外部,就是只加一次锁。
2.自旋锁:互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程都需要转入内核中完成。现将本该要阻塞的线程不去挂起,不放弃处理器的执行时间,而是在那做一个忙循环(自旋),看看持有锁的线程是否很快释放锁。自旋的次数(循环的次数)是有限度的,默认是10次,如果没有获得锁就采用传统的方式去挂起线程。
3.轻量级锁:在线程没有竞争的时候,采用CAS操作,避免使用互斥量的开销。这里涉及到对象头的概念。
4.偏向锁:它相对于轻量级锁,减少了锁重入的开销,对于第一个获得锁的线程,后面的执行如果该锁没有被其他线程获取,则该线程将不再进行同步(CAS操作)。
轻量级锁和偏向锁都是在没有竞争的情况下出现,一旦出现竞争就会升级为重量级锁。
对于synchronized,锁的升级情况可能是 偏向锁—>轻量锁—>自适应自旋锁—>重量锁
参考:
《深入理解Java虚拟机》
http://www.majin163.com/2014/03/17/synchronized2/ 这一篇博文写了synchronized的原理,它的实现和性能怎么样可以看看这一篇。
转自:https://blog.csdn.net/u013080921/article/details/42676231