原子操作

原子操作是一次性執行的操作,在原子操作執行期間沒有任何其他執行緒觀察或修改狀態的機會。

讓我們考慮一個不好的例子

private static int t = 0;

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(400); // The high thread count is for demonstration purposes.
    for (int i = 0; i < 100; i++) {
        executorService.execute(() -> {
            t++;
            System.out.println(MessageFormat.format("t: {0}", t));
        });
    }
    executorService.shutdown();
}

在這種情況下,有兩個問題。第一個問題是後增量運算子不是原子的。它由多個操作組成:獲取值,將值加 1,設定值。這就是為什麼如果我們執行這個例子,很可能我們不會在輸出中看到 t: 100-兩個執行緒可以同時獲取值,遞增它並設定它:假設 t 的值是 10,並且兩個執行緒是遞增 t。兩個執行緒都將 t 的值設定為 11,因為第二個執行緒在第一個執行緒完成遞增之前觀察到 t 的值。

第二個問題是我們如何觀察 t。當我們列印 t 的值時,在該執行緒的增量操作之後,該值可能已被另一個執行緒更改。

為了解決這些問題,我們將使用 java.util.concurrent.atomic.AtomicInteger ,它有許多原子操作供我們使用。

private static AtomicInteger t = new AtomicInteger(0);

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(400); // The high thread count is for demonstration purposes.
    for (int i = 0; i < 100; i++) {
        executorService.execute(() -> {
            int currentT = t.incrementAndGet();
            System.out.println(MessageFormat.format("t: {0}", currentT));
        });
    }
    executorService.shutdown();
}

incrementAndGet 方法 AtomicInteger 原子的增量,並返回的新值,從而消除前面的競爭狀態。請注意,在此示例中,行仍然會出現故障,因為我們不會對 println 呼叫進行排序,並且這不屬於此示例的範圍,因為它需要同步,此示例的目標是顯示如何使用 AtomicInteger 來消除有關國家的種族狀況。