一个没有并发控制的计数器:
public class Counter implements Runnable {
private static int count;
public void run() {
System.out.println(Thread.currentThread().getName()
+ ":" + (++count));
}
public static void main(String[] args){
Counter counter = new Counter();
Thread t1 = new Thread(counter);
Thread t2 = new Thread(counter);
Thread t3 = new Thread(counter);
Thread t4 = new Thread(counter);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
有时运行正常,但是偶尔会出现如下运行结果:
Thread-1:2
Thread-0:1
Thread-2:3
Thread-3:3
这显然和预期结果不太一样,先用javap -verbose命令分析一下这个类,在字节码层面上,++count等价于虚拟机顺次执行如下5条字节码指令(不考虑运行期的优化)
getstatic 获取指定类的静态域,并将其值压入栈顶
iconst_1 将int型1推送至栈顶
iadd 将栈顶两int型数值相加并将结果压入栈顶
dup 复制栈顶数值并将复制值压入栈顶
putstatic 为指定类的静态域赋值
当Thread-3线程执行getstatic指令时,Thread-2线程还未执行至iadd指令,故Thread-3线程获取的初始静态域count的值和Thread-2线程一样,都为2
本质原因就是++count虽然只是一行代码,但这一过程并非原子操作
要保证这种类型的原子操作,可以使用java.util.concurrent.atomic包下的类
软件包 java.util.concurrent.atomic
类的小工具包,支持在单个变量上解除锁的线程安全编程。
示例如下:
public class Counter implements Runnable {
private final AtomicInteger count = new AtomicInteger(0);
public void run() {
System.out.println(Thread.currentThread().getName()
+ ":" + count.incrementAndGet());
}
public static void main(String[] args){
Counter counter = new Counter();
Thread t1 = new Thread(counter);
Thread t2 = new Thread(counter);
Thread t3 = new Thread(counter);
Thread t4 = new Thread(counter);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
看看源代码中究竟是如何实现的
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final int get() {
return value;
}
是不是和乐观锁很像,如果结果符合预期结果,就将结果返回,否则不断进行重试,并没有进行同步,兼顾了安全性和性能
java.util.concurrent.atomic包下还有很多类,使用这些类可以保证对这些类的诸如“获取-更新”操作是原子性的,从而避免发生竞态条件
AtomicBoolean 可以用原子方式更新的 boolean 值。
AtomicInteger 可以用原子方式更新的 int 值。
AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
AtomicLong 可以用原子方式更新的 long 值。
AtomicLongArray 可以用原子方式更新其元素的 long 数组。
AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
AtomicReference<V> 可以用原子方式更新的对象引用。
AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。
AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。