在java并发编程里要实现原子操做,你们都会想到同步(synchronize),这种同步会使线程左塞,在性能上不是很满意。基于CAS(Compare And Set) 能够实现非左塞同步算法。 java
何为cas? 算法
compare-and-swap (CAS) is an atomic instruction used in multithreading to achieve synchronization
简单来讲就是原子操做,系统支不支持CAS还要看cpu,如今几乎全部的CPU指令都支持CAS的原子操做,X86下对应的是 CMPXCHG 汇编指令。cas指令须要三个操做数,非别是内存位置(V),旧值预期值(A)和新值。cas指令执行时,当且仅当V符合旧预期值A是,处理器用新的值B更新V的值,不然不执行更新。 编程
jdk1.5为咱们提供java.util.concurrent并发包,提供很方便的原子操做类,底层实现由sun.misc.Unsaft类里的compareAndWapInt和compareAndLong等几个方法封装调用,注意sun.misc.Unsaft是非标准的java api 。 api
非阻塞的计数器 多线程
使用AtomicInteger的compareAndSet方法,很是简单实现非左塞的计数器,代码比用同步块实现多线程的计数器简单的多。 并发
public class NonblockingCounter { private AtomicInteger value; public int getValue() { return value.get(); } public int increment() { int v; do { v = value.get(); while (!value.compareAndSet(v, v + 1)); return v + 1; } }
compareAndSet在一个死循环中,不断尝试将一个比当前值大1 赋值给本身,若是执行失败,说明有其余线程修改了值,只要从新循环执行一次操做,直到成功为止。 性能
非阻塞的简单链表 this
使用AtomicReference实现一个简单的链表操做(add),整体类结构和普通的链表差很少。 atom
public class ConcurrentLinked<E> { private AtomicReference<Node<E>> first = new AtomicReference<Node<E>>(); public boolean add(E e) { Node<E> n = new Node<E>(e); while (first.get() == null && first.compareAndSet(null, n)) { return true; } for (;;) { Node<E> insertNode = findInsertionPlace(); if (insertNode.next.compareAndSet(null, n)) { break; } } return true; } public E getLast() { Node<E> a = getFirstNode(); if (a == null) return null; while (a.next.get() != null) { // 找插入位置 a = a.next.get(); } return a.item; } private Node<E> getFirstNode() { return this.first.get(); } private Node<E> findInsertionPlace() { Node<E> a = getFirstNode(); while (a.next.get() != null) { // 找插入位置 a = a.next.get(); } return a; } private static class Node<E> { E item; AtomicReference<Node<E>> next = new AtomicReference<Node<E>>(); public Node(E item) { this.item = item; } } }
cas看似很完美,可是不是全部的原子操做场景都适合,好比,多个变量同步原子更新。使用cas的地方,通常会把compareAndSet放到一个无限的循环中,在线程竞争比较激烈的状况下,cpu消耗比较严重的。另外,cas自己有一个逻辑漏洞(俗称"ABA"问题)。 spa
参考资料
《java并发编程实践》