Javaの原子クラス


概要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

非スレッドセキュリティカウンタテスト結果と分析
結果を期待するCountthreadNum*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の使用はスレッドの安全を確保したが,より多くの時間を費やしたことが明らかになった.後でAtomicIntegeraddAndGet()メソッドのソースコード分析を行います.
    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でのロックフリープログラミングは、volatileUnsafeを組み合わせたCASを使用してスレッドのセキュリティを確保するのが基本ですが、CASではABAの問題を処理できません.また、CASは、より大きなCPUのオーバーヘッドをもたらします.
    スレッドセキュリティカウンタがより多くの時間を費やしたのは、CASの失敗によるものです.AtomicIntegerを紹介しただけであるが,実際には他の原子類内部で実現される原理はAtomicIntegerと類似している.