Javaの原子クラス
4353 ワード
概要
原子内部では
インスタンス分析
次に、カウンタの例を用いて、原子系
非スレッドセキュアカウンタ
テストケースコード
テスト用例コードを実行するときに何度も実行して出力結果を比較することができ、出力結果が近いと、このような出力結果が相対的に正確になります.
非スレッドセキュリティカウンタテスト結果と分析
結果を期待する
実行結果
ぶんせき
例中の を読み出す. を修正する. を再書き込み
実際に実行すると,複数のスレッドが同じiの古い値を読み出して修正後に書き込む可能性があるため,予想される結果と一致しない場合がある.
スレッドセキュアカウンタ
次に、非スレッドセキュリティのカウンタクラスでは、原子クラス
スレッドの安全なカウンタのテスト結果と分析
結果を期待する
実行結果
ぶんせき
原子系
AtomicIntegerソース分析
スレッドセキュリティカウンタがより多くの時間を費やしたのは、
java.util.concurrent.atomic
パケットの下にJava
によって提供されるスレッドの安全な原子クラスがある.原子内部では
volatile
を用いて可視性と秩序性を確保し、Unsafe
が提供するCAS
法を用いて原子性を確保した.インスタンス分析
次に、カウンタの例を用いて、原子系
AtomicInteger
の使用を説明し、AtomicInteger
の一部のソースコードを簡単に分析する.非スレッドセキュアカウンタ
package com.rjh.threadsafe;
/**
*
* @author RJH
* 2017 11 30
*/
public class Counter {
/**
* 0
*/
private int i=0;
/**
*
* @author RJH
* @return
*/
public int increase(){
return ++i;
}
/**
*
* @author RJH
* @return
*/
public int currentCount(){
return i;
}
}
テストケースコード
テスト用例コードを実行するときに何度も実行して出力結果を比較することができ、出力結果が近いと、このような出力結果が相対的に正確になります.
package com.rjh.threadsafe;
/**
*
* @author RJH
* 2017 11 30
*/
public class CounterTest {
public static void main(String[] args) {
//
Counter counter=new Counter();
//
int threadNum=1000;
//
int times=10000;
long start=System.currentTimeMillis();
for(int i=0;i1){
}
System.out.println("TotalTime:"+(System.currentTimeMillis()-start));
System.out.println("Count:"+counter.currentCount());
}
/**
* ,
* @author RJH
* 2017 11 30
*/
private static class ClickThread extends Thread{
/**
*
*/
private Counter counter;
/**
*
*/
private int times;
public ClickThread(Counter counter,int times) {
this.counter = counter;
this.times=times;
}
@Override
public void run(){
for(int i=0;i
非スレッドセキュリティカウンタテスト結果と分析
結果を期待する
Count
はthreadNum*times
、すなわち1000X10000=10000000
であるべきである実行結果
TotalTime:56
Count:9947180
ぶんせき
例中の
++i
は原子的ではなく、このコードは3
ステップ動作を実行する.i
の旧値i
の値i
の新しい値実際に実行すると,複数のスレッドが同じiの古い値を読み出して修正後に書き込む可能性があるため,予想される結果と一致しない場合がある.
スレッドセキュアカウンタ
次に、非スレッドセキュリティのカウンタクラスでは、原子クラス
AtomicIntege
rを用いてこのカウンタクラスをスレッドセキュリティに変更する. package com.rjh.threadsafe;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author RJH
* 2017 11 30
*/
public class Counter {
/**
* 0
*/
private AtomicInteger i=new AtomicInteger(0);
/**
*
* @author RJH
* @return
*/
public int increase(){
return i.incrementAndGet();
}
/**
*
* @author RJH
* @return
*/
public int currentCount(){
return i.get();
}
}
スレッドの安全なカウンタのテスト結果と分析
結果を期待する
Count
はthreadNum*times、すなわち1000 X 10000000=1億円であるべきである実行結果
TotalTime:625
Count:10000000
ぶんせき
原子系
AtomicInteger
の使用はスレッドの安全を確保したが,より多くの時間を費やしたことが明らかになった.後でAtomicInteger
のaddAndGet()
メソッドのソースコード分析を行います.AtomicIntegerソース分析
/**
* 1。
* @return
*/
public final int incrementAndGet() {
for (;;) {// while(true)
int current = get();//
int next = current + 1;//
if (compareAndSet(current, next))// ( ),
return next;
}
}
/**
*
*
* @param
* @param
* @return true, false
*/
public final boolean compareAndSet(int expect, int update) {
//Unsafe CAS,valueOffset Unsafe.objectFieldOffset()
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
CAS
が失敗するたびに現在の値が期待値として取得され、更新はCAS
によって原子性が確保される.Javaでのロックフリープログラミングは、volatile
とUnsafe
を組み合わせたCAS
を使用してスレッドのセキュリティを確保するのが基本ですが、CAS
ではABA
の問題を処理できません.また、CAS
は、より大きなCPU
のオーバーヘッドをもたらします.スレッドセキュリティカウンタがより多くの時間を費やしたのは、
CAS
の失敗によるものです.AtomicInteger
を紹介しただけであるが,実際には他の原子類内部で実現される原理はAtomicInteger
と類似している.