【转+原创】 AtomicLong原理解析
一 锁的机制
自从1.5后引入了Atomic+数据类型的类,具体在package java.util.concurrent.atomic包中,通过包名concurrent可以猜到,这个是线程安全的,既然线程安全可以用synchronized来解决,为什么还需要这些类呢?
乐观锁与悲观锁
独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
通过上段引用我们就可以猜测到AtomicLong一定是采用了乐观锁来解决的。这也是高效率的一种手段。
说道乐观锁,以前接触的是hibernate的乐观锁:
1.1hibernate的乐观锁
hibernate乐观锁在更新一行数据的时候认为这个数据是没有其他人修改的,并且记录这个数据的版本号,经过一系列的操作后,开始更新,update tablename set a=‘1’ where id=‘10’ and version=‘10’。如果其他人修改了这条记录,那么version就会从10变成11,再次调用上个语句,肯定更新不了,这个就是乐观锁的原理。
CAS锁
要实现无锁(lock-free)的非阻塞算法有多种实现方法,其中 CAS(比较与交换,Compare and swap) 是一种有名的无锁算法。CAS, CPU指令,在大多数处理器架构,包括IA32、Space中采用的都是CAS指令,CAS的语义是“我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”,CAS是项 乐观锁 技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。CAS无锁算法的C实现如下:
do{
备份旧数据;
基于旧数据构造新数据;
}while(!CAS( 内存地址,备份的旧数据,新数据 ))
就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后重新执行刚才的操作。容易看出 CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的 commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。
二、JDK1.8源码分析
public static void main(String[] args) {
AtomicLong atomicLong= new AtomicLong(1);
System.out.println(atomicLong.incrementAndGet());
}
追踪incrementAndGet
//其中valueOffset就是内存中的值,jdk1.8和1.7之前做了修改
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
继续追踪getAndAddLong方法
public final long getAndAddLong(Object this, long valueOffset, long step) {
long expect;//期待的值
do {
//其实就是把内存中的值复制给期待的值
expect= this.getLongVolatile(this, valueOffset);
//下面这while是重点,比较valueOffset是否和expect相等,如果相等认为没有其他线程改变这个值
} while(!this.compareAndSwapLong(this, valueOffset, expect, valueOffset+ step));
return expect;
}