Java は、マルチスレッド環境における数値安全性カウンター用にいくつかのクラスを設計しました。これらのクラスの一部は次のとおりです。
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
以下は、junit4 を使用した、マルチスレッド下での AtomicInteger と通常の int 値を比較する増分テストです。
完全なコード:
パッケージ test.java;import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicInteger;import org.junit.Assert;import org.junit.Before;import org.junit.Test;/** *マルチスレッドで AtomicInteger と通常の int 値のインクリメント操作をテストします*/public class TestAtomic { // Atomic Integer increment object public static AtomicInteger counter_integer; // = new AtomicInteger(0); // int 型の変数 public static int count_int = 0; @Before public void setUp() { // すべてのテストを開始する前に初期設定作業を実行します counter_integer = new AtomicInteger(0) ); } @Test public void testAtomic() throws InterruptedException { // 作成されたスレッドの数 int threadCount = 100;他の関連するスレッドが内部でループする回数 int loopCount = 10000600 // 関連するスレッドの補助オブジェクトを制御します (他の await スレッドはメイン スレッドの開始を待ちます) CountDownLatch latch_1 = new CountDownLatch(1);メインスレッドのオブジェクト Object; (メインスレッドはすべての補助スレッドの実行が完了するまで待機してから続行します) CountDownLatch latch_n = new CountDownLatch(threadCount) // 他の補助スレッドを作成して開始します。 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); } long startNano = System.nanoTime(); // 待機中の他のスレッドを開始します。 ; // 他のスレッドが終了するまで待機します latch_n.await() // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("sum が threadCount *loopCount に等しくありません。テストは失敗しました", sum, threadCount *loopCount); ); 等しいと予想されます-----------"); System.out.println("時間がかかります: " + ((endNano - startNano) / (1000 * 1000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (合計) + ";"); } @Test public void testIntAdd() throws InterruptedException { // 作成されたスレッドの数 int threadCount = 100;他の関連するスレッドが内部でループする回数 int loopCount = 10000600 // 関連するスレッドの補助オブジェクトを制御します (他の await スレッドはメイン スレッドの開始を待ちます) CountDownLatch latch_1 = new CountDownLatch(1);メインスレッドのオブジェクト Object; (メインスレッドはすべての補助スレッドの実行が完了するまで待機してから続行します) CountDownLatch latch_n = new CountDownLatch(threadCount) // 他の補助スレッドを作成して開始します。 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n,loopCount); } long startNano = System.nanoTime(); // 待機中の他のスレッドを一律に開始させる ( ); // 他のスレッドが終了するまで待機します latch_n.await(); // long endNano = System.nanoTime(); // Assert.assertNotEquals( "sum = threadCount *loopCount, testIntAdd() test failed", sum, threadCount *loopCount); System.out.println("------testIntAdd(); 2 つは等しくないことが予期されました。 - -------"); System.out.println("時間がかかります: " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (sum) + ";"); } // スレッド クラス AtomicIntegerThread は Thread { private CountDownLatch latchdown = null; AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount =loopCount; } @Override public void run() { // 信号の同期を待つ try { this.latch . await(); } catch (InterruptedException e) { e.printStackTrace() } // for (int i = 0; i < loopCount; i++) { counter_integer.getAndIncrement(); } // 通知は 1 つ減ります latchdown.countDown() } } // スレッド クラス IntegerThread extends Thread { private CountDownLatch latchdown = null; ; public IntegerThread(CountDownLatch ラッチ、CountDownLatch ラッチダウン、int ループカウント) { this.latch = ラッチ; this.latchdown = latchdown; this.loopCount =loopCount; } @Override public void run() { // 信号の同期を待つ try { this.latch.await() } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i <loopCount; i++) { count_int++; } // 通知は 1 つ減ります latchdown.countDown() } }}
通常の PC での実行結果は次のようになります。
----------testAtomic(); 2 つは等しいと予想されます -------時間のかかる: 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;----------------testIntAdd(); 2 つは等しいことが期待されません-------------- ----時間がかかります: 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988;
このことから、AtomicInteger 演算と int 演算の効率の差は約 50 ~ 80 倍であることがわかります。もちろん、この比較は参考値を提供するだけです。
シングルスレッド実行であると判断された場合は、int を使用する必要があります。マルチスレッドでの int 演算の実行効率は非常に高く、10 億回実行しても 1.5 秒しかかかりません。
(CPU が 2GHZ、デュアルコア、4 スレッドで、理論上の最大値が 8GHZ であると仮定すると、理論上は 1 秒あたり 80 億クロック サイクルになります。
10 億の Java int 増分には 1.5 秒かかり、計算すると 120 億の操作が各サイクルで 12 CPU サイクルを消費します。
個人的にはC言語でも4クロックサイクル以上(判定、内部コード実行、自動インクリメント判定、ジャンプ)が必要だと思います。
前提条件は、JVM と CPU は根本的に最適化されていないということです。
)
実際、AtomicInteger の効率は低くはありません。10 億回では 80 秒かかり、100 万回では約 1,000 分の 1 の 80 ミリ秒になります。