Java多线程:新特性—原子变量和CAS
硬件对并发的支持
现在,几乎所有的现代处理器中都包含了某种形式的原子读-改-写指令,例如比较和交换(CAS
)或者关联加载和条件存储(Load-Linked/Store-Conditional
)。
操作系统和JVM可以使用这些指令来实现锁和并发的数据结构。例如Java的ConcurrentlinkedQueue
就是使用CAS实现的。
Java5以后也提供了对这些指令的支持。当多个线程尝试使用CAS同时更新同一个变量时候,只有一个线程能更新变量的值,而其他线程都将失败。
然而,失败的线程并不会被挂起(这与获取锁的情况不同:当获取锁失败时,线程将会被挂起),而是被告知在这次竞争中失败,并且可以重试。
由于一个线程在竞争CAS时失败不会被阻塞,因此它可以决定是否重新尝试,或者执行一些恢复操作,也或者不执行任何操作。
这种灵活性就大大减少了与锁相关的活跃性风险。
实际上,当竞争程度不高时,基于CAS的计数器在性能上远远高于基于锁的计数器,而在没有竞争时候甚至更加高。
CAS的主要缺点是:它将使调用者处理竞争问题(通过重试、回退、放弃),而在锁中能自动处理竞争问题(线程在获得锁之前将一直阻塞)。也可能产生ABA
问题。
锁的劣势
若使用锁机制,当多个线程同时请求锁,那么JVM就需要借助操作系统的功能。
那么一些线程将会被挂起并且在稍后恢复运行。当线程恢复执行时,必须等待其他线程执行完它们的时间片以后,才能被调度执行。
在挂起和恢复线程等过程中存在着很大的开销,并且通常存在着较长时间的中断。
与锁相比,volatile
变量是一种更加轻量级的同步机制,因为在使用这些变量时候不会发生上下文切换或者线程调度等操作。
Java5之后,专门提供了用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic
,称为原子变量类。
原子变量类直接利用了硬件对并发的支持,采用CAS算法提供了更高的可伸缩性。
例子
使用原子变量不再需要对变量加锁,例如:
|
|