在java.util.concurrent包的许多类中,例如Semaphore和ConcurrentLinkedQueue,都提供了比Synchronized机制更高的性能和可伸缩性。这种性能提升的主要来源:原子变量和非阻塞的同步机制。
近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令代替锁来确保数据在并发访问中的一致性。非阻塞算法被广泛应用于在操作系统和JVM中实现线程/进程调度机制、垃圾回收机制以及锁和其他并发数据结构。
由于非阻塞算法可以使多个线程在竞争相同的数据时不会发生阻塞,因此它能在粒度更细的层次上进行协调,并且极大的减小调度开销。而且,在非阻塞算法中不存在死锁和其他活跃性问题。从Java 5.0开始,可以使用原子变量类(例如AtomicInteger和AtomicReference)来构建高效的非阻塞算法。
即使原子变量没有用于非阻塞算法的开发,它们也可以用做一种“更好的Volatile类型变量”。原子变量提供了与Volatile类型变量相同的内存语义,此外还支持原子的更新操作,从而使它们更加适用于实现计数器、序列发生器等,同时还能比基于锁的方法提供更高的可伸缩性。
1、硬件对并发的支持
在针对多处理器操作而设计的处理器中提供了一些特殊指令,用于管理对共享数据的并发访问。在早期的处理器中支持原子的测试并设置,获取并递增以及交换等指令,这些指令足以实现各种互斥体,而这些互斥体又可以实现一些更加复杂的并发对象。现在,几乎所有的现代处理器中都包含了某种形式的原子读-改-写指令,例如比较并交换或者关联加载/条件存储。操作系统和JVM使用这些指令来实现锁和并发的数据结构。
1.1、比较并交换
public class SimulatedCAS{
private int value;
public synchronized int get(){
return value;
}
public synchronized int compareAndSwap(int expectedValue,int newValue){
int oldValue = value;
if(oldValue == expectedValue){
value = newValue;
return oldValue;
}
}
}
当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都将失败。然而失败的线程并不会被挂起,而是被告知在这次的竞争中失败,并可以再次尝试。由于CAS能检测到来自其他线程的干扰,因此即使不使用锁也能实现原子的读 - 改 - 写操作。
1.2、JVM对CAS的支持
1.3、原子变量类
1.4、非阻塞算法
在非阻塞的算法中通常不会出现死锁和优先级反转(非公平显式锁等)问题。在许多常见的数据结构中都可以使用非阻塞算法,包括栈、队列、优先级队列以及散列表等,而要设计一些新的数据结构,最好还是由专家们来完成。
1.5、原子的域更新器
原子的域更新器类表示现有的volatile域的一种基于反射的“视图”,从而能够在已有的volatile域上使用CAS。在更新器类中没有构造函数,要创建一个更新器对象,可以调用newUpdater工厂方法,并制定类和域的名字。域更新器类没有与某个特定的实例关联在一起,因而可以更新目标类的任意实例中的域。更新器类提供的原子性保证比普通原子类更弱一些,因为无法保证底层的域不被直接修改 - compareAndSet以及其他算术方法只能确保其他使用原子域更新器方法的线程的原子性。
private class Node<T>{
private final E item;
private volatile Node<T> next;
public Node(T item){
this.item = item;
}
}
private static AtomicReferenceFieldUpdater<Node,Node> nextUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class,Node.class,"next"