スレッドが安全でブロックされていないAtomicクラス
4627 ワード
原子操作AtomicInteger
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(...)実現したのです
テストコード:
ここで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つのステップが含まれているからです.
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つのステップが含まれているからです.