在大多数情况下,我们为了实现线程安全都会使用Synchronized或lock来加锁进行线程的互斥同步,但互斥同步的最主要的问题就是进行线程的阻塞和唤醒所带来的性能问题,因此这种阻塞也称作阻塞同步。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都会进行加锁、用户态核心态转换、维护锁的计数器和检查是否有被阻塞的线程需要被唤醒等操作。
非阻塞同步
随着硬件指令集的发展,我们有了另一个选择:基于冲突检测的乐观并发策略,通俗的说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功;如果共享数据有争用,产生了冲突,那就再采用其他的补偿措施(最常见的就是不断的重试,直到成功),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步措施称为非阻塞同步。
原子类Atomic
在JDK 1.5以后,Java程序提供了CAS(比较和交换),其实我们在前面文章已经见过CAS操作了,并发包下的并发队列的原理就是CAS操作,可以查看这篇文章:Java并发编程-并发队列(ConcurrentLinkedQueue)的原理分析
当CAS指令执行时,会先将内存的值进行保存,当操作完成时再判断保存的值和当前内存的值是否相同,如果不同则说明其他线程操作了该数据,所以需要再次进行操作直到成功。在java.util.concurrent.atomic包中提供的原子类Atomic实现CAS操作。
原子操作类共有13个类,在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。下面将分别介绍这四种原子操作类。
原子更新基本类型
使用原子方式更新基本类型,共包括3个类:
- AtomicBoolean:原子更新布尔变量
- AtomicInteger:原子更新整型变量
- AtomicLong:原子更新长整型变量
常用的方法如下,以AtomicInteger源码为例进行说明(JDK 1.7):
public class AtomicInteger extends Number implements java.io.Serializable {
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
}
//返回当前值
public final int get() {
return value;
}
//设置新的值,非原子操作
public final void set(int newValue) {
value = newValue;
}
//最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
//原子操作,取当前的值,并设置新的值,返回旧值
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
//如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//以原子方式将当前值加 1,返回旧值