スレッドが安全でブロックされていないAtomicクラス

4627 ワード

原子操作AtomicInteger
 
public class AtomicLong extends Number  
                implements Serializable

 
 
J 2 SE 5.0は、同期処理の簡略化を支援するatomic classのセットを提供しています.
基本的な動作原理はsynchronizedを同期する方法を用いて1つのlong,integerに対して、オブジェクトの増減、賦値(更新)操作を実現した.例えば、++演算子の場合、AtomicIntegerは、それが保有するintegerをatomic的に増加させることができる.2つ以上のatomic変数にアクセスする必要があるプログラムコード(または単一のatomic変数に対して2つ以上の操作を実行する)は、通常、synchronizeによって両方の操作が1つのatomicのユニットと見なすことができるようにする必要がある. 
 
簡単に言えば、これらのクラスはスレッドが安全で、ブロックなしでロックなしをサポートしています.
 
Javaコード
addAndGet(long delta)   
//与えられた値を現在の値に原子的に追加します.  
  
getAndSet() :   
//新しい値を設定し、古い値を返します.   
  
compareAndSet(expectedValue, newValue) :   
//現在値==予想値の場合、その値を所定の更新値に原子的に設定する
//更新に成功した場合、trueを返します.そうしないとfalse を返します.
//言い換えれば、原子変数を新しい値に設定しますが、前回見た変数から現在まで他のスレッドに変更された場合(私が望んでいる値と一致しません)、更新に失敗しました.
/*単一スレッドでは、compareAndSetは常にtrue、を返します.
*マルチスレッドでresultとcompareを行う場合、counterは他のスレッドに新しい値をセットされている可能性があります.この場合、もう一度取り出して比較する必要があります.*まだ最新の値を取得していない場合は、最新の値を取得するまでループします.
    */  
 
パフォーマンスを向上させるために、AtomicLongなどのクラスは、同期を実現する際にsynchronizedキーワードではなく、最下位層(ローカルc言語実装コード)を直接使用して完成し、
synchronizedキーワードは見えません
たとえば、AtomicLongクラスの原子操作方法:
public final boolean compareAndSet(long expect, long update) ;
SUN社の低層ローカルコードを直接使用する原子法(native法):
public final native boolean compareAndSwapLong(...)実現したのです
 
 
 
set()  
get()  
getAndSet()  
getAndIncrement()  
getAndDecrement()  
getAndAdd()

 
 
テストコード:
 
import java.util.concurrent.CountDownLatch;  
import java.util.concurrent.atomic.AtomicLong;  
  
import org.junit.Test;  

public class AtomicTest {  
  
    @Test  
    public void testAtomic()  
    {  
        final int loopcount = 10000;  
        int threadcount = 10;  
  
        final NonSafeSeq seq1 = new NonSafeSeq();  
        final SafeSeq seq2 = new SafeSeq();  
  
        final CountDownLatch l = new CountDownLatch(threadcount);  
  
        for(int i = 0; i < threadcount; ++i)  
        {  
            final int index = i;  
            new Thread(new Runnable() {  
  
                @Override  
                public void run() {  
                    for(int j = 0; j < loopcount; ++j)  
                    {  
  
                        seq1.inc();  
                        seq2.inc();  
                    }  
  
                    System.out.println("finished : " + index);  
                    l.countDown();  
  
                }  
            }).start();  
        }  
  
        try {  
            l.await();  
        } catch (InterruptedException e) {  
  
            e.printStackTrace();  
        }  
  
        System.out.println("both have finished....");  
  
        System.out.println("NonSafeSeq:" + seq1.get());  
        System.out.println("SafeSeq with atomic: " + seq2.get());  
  
    }  
}  
  
class NonSafeSeq{  
    private long count = 0;  
    public void inc()  
    {  
        count++;  
    }  
  
    public long  get()  
    {  
        return count;  
    }  
}  
  
class SafeSeq{  
    private AtomicLong count  = new AtomicLong(0);  
  
    public void inc()  
    {  
        count.incrementAndGet();  
    }  
  
    public long get()  
    {  
        return count.longValue();  
    }  
} 

 
 
ここでNonSafeSeqは対比のクラスとして、private long countを直接置くのはスレッドが安全ではありませんが、SafeSeqにはAtomicLongが入っていて、スレッドが安全です.incrementAndGetを直接呼び出して追加できます
 
コードを実行すると、このような結果が得られます.
finished : 1 
finished : 0 
finished : 3 
finished : 2 
finished : 5 
finished : 4 
finished : 6 
finished : 8 
finished : 9 
finished : 7 
both have finished.... 
NonSafeSeq:91723 
SafeSeq with atomic: 100000 
 
10個のスレッドは、各スレッドが10000回実行され、理論的には100000回増加するはずであり、通常のlongを使用すると非スレッドが安全であり、AtomicLongを使用するとスレッドが安全であることがわかる. 
 
なお、この例は、long自体の単一設定が原子的であるか、成功するか、成功しないかのいずれかであるが、count++のような動作はスレッドが安全ではないことを示している.これには、読み取りと書き込みの2つのステップが含まれているからです.