マルチスレッド2:Java原子変数(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は1行のコードであるが,この過程は原子操作ではない.
このタイプの原子操作を保証するには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       “  ”     ,             。