在JAVA中透過synchronized語句可以實現多執行緒並發。使用同步程式碼區塊,JVM保證同一時間只有一個執行緒可以擁有某一物件的鎖。鎖機制實作了多個執行緒安全地對臨界資源進行存取。
同步程式碼寫法如下:
代碼1:
Object obj = new Object(); ... synchronized(obj) { //TODO: 存取臨界資源} JAVA的多執行緒總是充滿陷阱,如果我們用Boolean作為被同步的對象,可能會出現以下兩種情況:
一. 以為對一個物件加鎖,實際同步的是不同物件。
代碼2:
private volatile Boolean isTrue = false; publich void aMethod() { ... synchronized(isTrue) { isTrue = !isTrue; //TODO: 存取臨界資源isTrue = !isTrue; } ... }咋一看上面的程式碼沒有問題,由於使用了synchronized(isTrue)同一時間只能有一個執行緒存取臨界資源,但事實並不是這樣。因為false和true這兩個常數對應著兩個不同的物件。當isTrue產生變化時,很可能會導致不同的執行緒同步了不同的物件。 JAVA的自動裝箱會將false變成Boolean.FALSE,將true變成Boolean.TRUE(同時這也說明了此處若將false改為Boolean.FALSE其結果也是一樣的)。寫出一個以上情況的測試程式碼如下:
代碼3:
public class BooleanTest { private volatile Boolean isTrue = Boolean.FALSE; //此處用false也一樣public void aMethod() { for(int i=0;i<10;i++) { Thread t = new Thread() { public void run() { synchronized(isTrue) { isTrue = !isTrue; System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue); try{ Double ran = 1000 * Math.random(); Thread.sleep(ran.intValue()); } catch(InterruptedException e) {} if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!"); isTrue = !isTrue; } } }; t.start(); } } public static void main(String... args) { BooleanTest bt = new BooleanTest(); bt.aMethod(); } }執行以上程式碼,不時的會看到" - Oh, No!",表示不同的執行緒同時進入到synchronized程式碼區塊中。
二. 以為同步的是不同對象,實際上是一個對象。
有時候我們可能希望在多個物件上進行同步,如果使用了Boolean作為被同步對象,很可能會導致本來應該沒有關係的兩個同步區塊使用了相同物件的鎖定。範例如下:
代碼4:
private volatile Boolean aBoolean = Boolean.FALSE; private volatile Boolean anotherBoolean = false; public void aMethod() { ... synchronized(aBoolean) { //MethodDO: 訪問臨界資源1 } ... } public void another() { . .. synchronized(anotherBoolean) { //TODO: 存取臨界資源2 } ... }假設原本aMethod和anotherMethod分別會被兩組沒有關係的線程呼叫。但由於Boolean.FALSE和false指向的是同一個對象,可能導致對臨界資源2的存取被臨界資源1阻塞了(反之亦然)。
以上兩種情況說明,在使用同步區塊時,盡量不用使用Boolean物件作為被同步對象,不然可能會出現意想不到的問題,或是對以後的程式碼修改造成陷阱。
從此也可以看出,任何對常數的同步都是有風險的。如果一定要對Boolean 進行同步,一定要用new 運算元來建立Boolean 物件。