最开始接触到相关的内容应该是从动荡的关键字开始的吧,知道它可以保证变量的可见性,而且利用它可以实现读与写的原子操作…但是要实现一些复合的操作波动就无能为力了…最典型的代表是递增和递减的操作....
我们知道,在并发的环境下,要实现数据的一致性,最简单的方式就是加锁,保证同一时刻只有一个线程可以对数据进行操作....例如一个计数器,我们可以用如下的方式来实现:
公开课计数器{ 私人波动int=0; 公共同步int incrAndGet (int数){ 这一点。+=数量; 返回一个; } 公共同步int get () { 返回一个; } }
我们对操作都用同步关键字进行修饰,保证对属性一个的同步访问…这样子确实可以保证在并发环境下一个的一致性,但是由于使用了锁,锁的开销,线程的调度等等会使得程序的伸缩性受到了限制,于是就有了很多无锁的实现方式....
其实这些无锁的方法都利用了处理器所提供的一些CAS(比较和开关)指令,这个CAS到底干了啥事情呢,可以用下面这个方法来说明中科院所代表的语义:
公共同步int compareAndSwap (int, int newValue) { int老=this.a; 如果(老==期望){ 这一点。一个=newValue; } 返回旧; }
好吧,通过代码应该对CAS语义的标书很清楚了吧,好像现在大多数的处理器都实现了原子的CAS指令了吧。
好啦,那么接下来来看看在java中中科院都用在了什么地方了吧,首先来看AtomicInteger类型吧,这个是并发库里面提供的一个类型:
私人动荡的int值;
这个是内部定义的一个属性吧,用于保存值,由于是挥发性类型的,所以可以保证线程之间的可见性以及读写的原子性…
那么接下来来看看几个比较常用的方法:
公众最终int addAndGet (intδ){ (,) { int电流=(); int下当前+δ=; 如果(compareAndSet(电流、下)) 返回下一个; } }
这个方法的作用是在当前值的基础上加上三角洲,这里可以看到整个方法中并没有加锁,这代码其实就算是java中实现无锁计数器的方法,这里compareAndSet方法的定义如下:
公众最终布尔compareAndSet (int, int更新){ 返回unsafe.compareAndSwapInt (valueOffset,期望、更新); }
由于调用了不安全的方法,所以这个就无能为力了,其实应该能猜到JVM调用了处理器本身的CAS指令来实现原子的操作…
基本上AtomicInteger类型的重要方法都是采用无锁的方式实现的,因此在并发环境下,用这种类型能有更好的性能。
上面算是搞定了在java中实现无锁的计数器,接下来来看看如何实现无锁栈,直接贴代码了,代码是从《java并发编程实战》中模仿下来的:
包并发; 进口java.util.concurrent.atomic.AtomicReference; 公开课ConcurrentStack{ AtomicReference 比;顶级=new AtomicReference 在(); 公共空间(E)项{推 Node newHead=new Node (项); Node oldHead; 而(真){ oldHead=top.get (); newHead。下一个=oldHead; 如果(top.compareAndSet (oldHead newHead)) { 返回; } } } 公共E pop () { 而(真){ Node oldHead=top.get (); 如果(oldHead==null) { 返回null; } Node newHead=oldHead.next; 如果(top.compareAndSet (oldHead newHead)) { 返回oldHead.item; } } } 私有静态类Node { 公众最终E项; 公共Node 下一个; 公共节点(E项){ 这一点。项=项目; } } }
好啦,上面的代码就算是实现了一个无锁的栈,简单吧…在并发环境中,无锁的数据结构伸缩性能够比用锁好得多。
在提到无锁编程的时候,就不得不提到无锁队列,其实在并发库中已经提供了无锁队列的实现:ConcurrentLinkedQueue,我们来看看它的重要的方法实现吧:
公共布尔提供(E E) { checkNotNull (e); 最后NodenewNode=new Node (e); (Node t=尾巴,p=t;;) { Node q=p.next; 如果(q==null) {//p是最后一个节点 如果(p。casNext (null, newNode)) {//成功的CAS是线性化点//e成为这个队列的一个元素,//和newNode成为“活”。 如果(p !=t)//跳两个节点 casTail (t, newNode);//失败是好的。 返回true; }//CAS比赛输给了另一个线程;重读一 } else if (p==)//我们有掉落列表。如果尾巴不变,它//也将off-list,在这种情况下我们需要//跳转到头部,所有节点都是生活//可及。其他新尾巴是一个更好的选择。 p=(t !=(t=尾))& # 63;t:头; 其他的//检查尾更新后两个啤酒花。 p=(p !=t,,t !=(t=尾))& # 63;t:问; } } Java语言中cas指令的无锁编程实现实例