這節提供一個不直覺的錯誤
package trial; import java.util.concurrent.TimeUnit; public class Trial { private static boolean stopRequested; public static void main(String[] args) throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { public void run() { int i = 0; while (!stopRequested) { i++; System.out.println(i); } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); stopRequested = true; } }
作者說在沒用 synchronization 機制的情況下, Java 沒有保證 thread 什麼時候看到彼此之間的修改。上面的例子, 在作者的機器上不會停。不過我自己試的結果是一秒後結束。作者說原因是 JVM 有可能最佳化 while 那行, 變成只檢查一次 stopRequested, 結果就不會停了。這個 VM 最佳化技巧叫做 hoisting (文中的例子則是改用 stack 上的變數取代 heap 上的)。
解法是宣告 stopRequested 時加上 volatile。不過作者後來接著強調 volatile 很難寫對, 叔叔有練過, 小朋友別亂用 volatile, 乖乖用 java.util.concurrent.atomic 下的類別, 像是 AtomicLong。這個 package 下的東西用了 machine-level 的方法減少 lock overhead, 比用 synchronized 有效率。
當然, 保險的話, 讀寫時都用 synchronized 最安全, 不過就會損失一些效率了。
沒有留言:
張貼留言