【Java基礎】併發

cryAllen發表於2016-06-28

Num1:同步訪問共享的可變資料

關鍵字Synchronized可以保證在同一時刻,只有一個執行緒可以執行某一個方法,或者某一個程式碼塊。、

同步不僅僅理解為互斥的方式,如果沒有同步,一個執行緒的變化就不能被其他執行緒看到。同步不僅可以阻止一個執行緒看到物件處於不一致的狀態中,它還可以保證進入同步方法或者同步程式碼塊的每個執行緒,都看到由同一個鎖保護之前的所有修改效果。

基本版本:

public class StopThread {
    private static boolean stopRequested;

    private static synchronized void requestStop() {
        stopRequested = true;
    }

    private static synchronized boolean stopRequested() {
        return stopRequested;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while (!stopRequested())
                    i++;
            }
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}

改善版本:

public class StopThread {
    private static volatile 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++;
            }
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

簡而言之,當多個執行緒共享可變資料的時候,每個讀或者寫資料的執行緒都必須執行同步。如果沒有同步,就無法保證一個執行緒所做的修改可以被另一個執行緒獲知,未能同步共享可變的資料或造成程式的活性失敗和安全性失敗。

Num2:executor和task優先於執行緒

如何建立一個工作佇列呢,一行程式碼。

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(runnable);
executor.shutdown();

如果想讓不止一個執行緒來處理來自這個佇列的請求,只要呼叫一個不同的靜態工廠,這個工廠建立了一種不同的executor service,稱作執行緒池(thread pool)

Num3:慎用延遲初始化

延遲初始化是延遲到需要域的是值時才將它初始化的這種行為。如果永遠不需要這個值,這個域就永遠不會被初始化。這種方法既適用於靜態域,也適用於例項域。雖然延遲初始化主要是一種優化,但它也可以用來打破類和例項初始化的有害迴圈。

  • 在大多數情況下,正常的初始化要優先於延遲初始化。

  • 如果利用延遲優化來破壞初始化的迴圈,就要使用同步的訪問方法。
  • 如果出於效能的考慮而需要對靜態域使用延遲初始化,就用lazy initialization holder class 模式。
  • 如果出於效能的考慮而需要對例項域使用延遲初始化,就用雙重檢查模式double-check idiom
  • 單重檢查模式single-check idiom

示例程式碼:

public class Initialization {

    // Normal initialization of an instance field - Page 282
    private final FieldType field1 = computeFieldValue();

    // Lazy initialization of instance field - synchronized accessor - Page 282
    private FieldType field2;

    synchronized FieldType getField2() {
        if (field2 == null)
            field2 = computeFieldValue();
        return field2;
    }

    // Lazy initialization holder class idiom for static fields - Page 283
    private static class FieldHolder {
        static final FieldType field = computeFieldValue();
    }

    static FieldType getField3() {
        return FieldHolder.field;
    }

    // Double-check idiom for lazy initialization of instance fields - Page 283
    private volatile FieldType field4;

    FieldType getField4() {
        FieldType result = field4;
        if (result == null) { // First check (no locking)
            synchronized (this) {
                result = field4;
                if (result == null) // Second check (with locking)
                    field4 = result = computeFieldValue();
            }
        }
        return result;
    }

    // Single-check idiom - can cause repeated initialization! - Page 284
    private volatile FieldType field5;

    private FieldType getField5() {
        FieldType result = field5;
        if (result == null)
            field5 = result = computeFieldValue();
        return result;
    }

    private static FieldType computeFieldValue() {
        return new FieldType();
    }
}

class FieldType {
}

相關文章