2011-11-23 171 views
1

第一類:這些類是線程安全的嗎?

class Main1 { 
    private ExecutorService service = Executors.newFixedThreadPool(4); 

    public static void main(String[] args) { 
     Main1 m = new Main1(); 
     m.start(); 
    } 

    public void start() { 
     final MyObject obj = new MyObject(); 
     obj.doSomeCalculation();// after this point not to modify obj in main thread 

     service.submit(new Runnable(){ 
      public void run() { 
        obj.doSomething(); // is it threadsafe doing this? 
       } 
     }); 
    } 
} 

二等:

class Main2 { 
    private ExecutorService service = Executors.newFixedThreadPool(4); 

    public static void main(String[] args) { 
     Main2 m = new Main2(); 
     m.start(); 
    } 

    public void start() { 
     class Job implements Runnable { 
      public MyObject obj; 

      public void run() { 
       obj.doSomething(); // is it threadsafe doing this? 
      } 
     } 

     Job job = new Job(); 
     job.obj.doSomeCalculation(); // after this point not to modify obj in main thread 
     service.submit(job); 
    } 
} 

Main1Main2線程? Main1和Main2對線程安全有不同的意義嗎?

更新: doSomeCalulation()和doSomething()都沒有任何鎖定或同步塊。我想知道doSomething()是否總能讀取doSomeCalculation()更改爲obj的正確狀態

回答

3

Main1,Main2線程安全嗎?

Main1的情況下,應用程序的線程安全取決於是否MyObject是線程安全的,是否有任何其他線程做的事情吧。但是,obj.doSomething();聲明是線程安全的,假設沒有其他更改對象

事實上,obj.doSomething();語句不使用封閉類中的變量。相反,該變量的值在隱藏的構造函數參數中傳遞給內部類。使這個線程安全的另一件事是在創建新線程時父線程和子線程之間存在隱式同步。 (參考 - JLS 17.4.4 Synchronization Order)這兩個事實相結合意味着Runnable.run()方法將獲得正確的引用,並且子線程將在同步點(或更高版本)中看到該對象的狀態。

Main2的情況下,同樣適用。在這種情況下,你只是明確地(或多或少)在Main1案例中隱含發生的事情。

UPDATE - 上述推理適用於即使您在將父對象傳遞給子線程之前在父線程中更改對象(因爲您已更新的問題)......因爲我提到了隱式同步。 (但是,如果父線程是在submit()呼叫後改變MyObject,你會遇到線程安全問題。)

不MAIN1和MAIN2使不同的感覺?

我不知道你在問什麼。如果你問使用內部類而不是匿名內部類是否有任何好處......在這種情況下答案是否定的。它們在線程安全方面表現相同。

其實,Main1版本更好,因爲它更簡單,更可讀的(一個有經驗的Java開發人員),以及更強勁。 Main2類公開obj字段,以便其他代碼可能訪問甚至更新。這是不好的風格。你可以修復這個問題,但只能通過添加更多的代碼......這使我們回到簡單/可讀性。

+0

我無法用英語表達良好,您的答案確實非常好。非常感謝。 – Jarod

+0

不客氣。 –

1

你的代碼結構的方式,提交您的工作(在這兩種情況下)已經執行上述計算之後。所以這兩種行爲沒有機會並行發生,所以沒有數據競賽。

但是,如果您要執行之後將作業/ Runnable提交給執行程序服務,那麼這兩個計算可能會並行發生,並且可能會發生數據競爭。

Job job = new Job(); 
service.submit(job); 
// Now there is a data race!!! 
job.obj = ...// do some calculation, and after this point not to modify obj in main thread 
+0

我剛剛更新了我的問題,指出doSomeCalulation()和doSomething()都沒有任何鎖定或同步塊。沒有鎖,沒有內存屏障(http://en.wikipedia.org/wiki/Memory_barrier)。我不確定doSomething()可以讀取另一個線程中doSomeCalculation()中更改的正確狀態。 – Jarod

+0

鎖或同步塊將幫助處理任何數據競爭和內存模型問題。如果沒有它們,你將會有數據競爭的可能性,並且可以預期你懷疑的行爲 - 一個線程不會看到另一個線程的影響或以意外的順序看到它。 – shams