多线程2:java.util.concurrent.atomic.*
一个没有并发控制的计数器:
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 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。