Java语言中cas指令的无锁编程实现实例

  

最开始接触到相关的内容应该是从动荡的关键字开始的吧,知道它可以保证变量的可见性,而且利用它可以实现读与写的原子操作…但是要实现一些复合的操作波动就无能为力了…最典型的代表是递增和递减的操作....

  

我们知道,在并发的环境下,要实现数据的一致性,最简单的方式就是加锁,保证同一时刻只有一个线程可以对数据进行操作....例如一个计数器,我们可以用如下的方式来实现:

        公开课计数器{   私人波动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)项{推   NodenewHead=new Node(项);   NodeoldHead;   而(真){   oldHead=top.get ();   newHead。下一个=oldHead;   如果(top.compareAndSet (oldHead newHead)) {   返回;   }   }   }   公共E pop () {   而(真){   NodeoldHead=top.get ();   如果(oldHead==null) {   返回null;   }   NodenewHead=oldHead.next;   如果(top.compareAndSet (oldHead newHead)) {   返回oldHead.item;   }   }   }   私有静态类Node{   公众最终E项;   公共Node下一个;      公共节点(E项){   这一点。项=项目;   }   }   }      

好啦,上面的代码就算是实现了一个无锁的栈,简单吧…在并发环境中,无锁的数据结构伸缩性能够比用锁好得多。
  在提到无锁编程的时候,就不得不提到无锁队列,其实在并发库中已经提供了无锁队列的实现:ConcurrentLinkedQueue,我们来看看它的重要的方法实现吧:

        公共布尔提供(E E) {   checkNotNull (e);   最后NodenewNode=new Node (e);   (Nodet=尾巴,p=t;;) {   Nodeq=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指令的无锁编程实现实例