2011年8月27日 星期六

Effective Java 讀書筆記: Item 66 - 用 synchronized 確保 concurrency

這節提供一個不直覺的錯誤

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 最安全, 不過就會損失一些效率了。

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...