一个没有并发控制的计数器:
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:2Thread-0:1Thread-2:3Thread-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基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。 AtomicLong 可以用原子方式更新的 long 值。 AtomicLongArray 可以用原子方式更新其元素的 long 数组。 AtomicLongFieldUpdater 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。 AtomicMarkableReference AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。 AtomicReference 可以用原子方式更新的对象引用。 AtomicReferenceArray 可以用原子方式更新其元素的对象引用数组。 AtomicReferenceFieldUpdater 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。 AtomicStampedReference AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。