多线程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 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。 
posted @ 2012-11-20 16:07  心意合一  阅读(133)  评论(0编辑  收藏  举报