原子操作
原子操作是一次性執行的操作,在原子操作執行期間沒有任何其他執行緒觀察或修改狀態的機會。
讓我們考慮一個不好的例子。
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
來消除有關國家的種族狀況。