2014-09-29 81 views
0

一個快速(我認爲)併發問題:我正在Udemy.com上通過一個多線程課程,老師通過下面的代碼進行了討論。雖然他解釋了它,但我仍不確定爲什麼要創建lock1lock2對象而不是鎖定在list1list2上。同步:多重鎖定 - 創建鎖定對象?

App.java:

public class App { 

    public static void main(String[] args) { 
     Worker worker = new Worker(); 
     worker.main(); 
    } 
} 

Worker.java:

import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 


public class Worker { 

    private Random random = new Random(); 

    private Object lock1 = new Object(); 
    private Object lock2 = new Object(); 

    private List<Integer> list1 = new ArrayList<Integer>(); 
    private List<Integer> list2 = new ArrayList<Integer>(); 

    public void stageOne() { 

     synchronized (lock1) { 
      try { 
       Thread.sleep(1); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      list1.add(random.nextInt(100)); 
     } 

    } 

    public void stageTwo() { 

     synchronized (lock2) { 
      try { 
       Thread.sleep(1); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      list2.add(random.nextInt(100)); 
     } 

    } 

    public void process() { 
     for(int i=0; i<1000; i++) { 
      stageOne(); 
      stageTwo(); 
     } 
    } 

    public void main() { 
     System.out.println("Starting ..."); 

     long start = System.currentTimeMillis(); 

     Thread t1 = new Thread(new Runnable() { 
      public void run() { 
       process(); 
      } 
     }); 

     Thread t2 = new Thread(new Runnable() { 
      public void run() { 
       process(); 
      } 
     }); 

     t1.start(); 
     t2.start(); 

     try { 
      t1.join(); 
      t2.join(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     long end = System.currentTimeMillis(); 

     System.out.println("Time taken: " + (end - start)); 
     System.out.println("List1: " + list1.size() + "; List2: " + list2.size()); 
    } 
} 
+2

沒有理由。你應該只能在'final'變量上同步。如果你可以將你的'List'標記爲'final',那麼你可以簡單地同步它。使用單獨的對象可以分離關注點,並可以考慮使代碼更清晰。 – 2014-09-29 11:42:54

+2

同意鮑里斯。有一點我會添加它,你永遠不想鎖定由getter返回的字段。因此,通過將鎖定與用於不同目的的任何字段隔離,可以消除這種可能性。 – 2014-09-29 11:47:22

+0

感謝@BoristheSpider和@John B.爲什麼只鎖定'final'變量?爲什麼不使用getters? – 2014-09-29 12:10:28

回答

4

我不認爲對的動機在你給的代碼表示,但它一般是最佳實踐。然而,同樣的最佳實踐要求鎖對象也是final

如果有問題的列表要麼從外部接受,要麼通過方法暴露給外部,那麼單獨鎖定對象的好處就會變得更加明顯:將鎖定暴露給外部代碼永遠不是一個好主意,因爲外來的代碼可以使用它們自己鎖定,打破自己的使用模式。

如果列表是嚴格私人的,那麼他們的監視器可用於內部鎖定;但是,稍後更改列表上的訪問策略可能會無意中影響鎖定策略。所以,從私人鎖開始也可以避免任何未來的錯誤。

+0

太棒了 - 在我看來,鎖定哪個對象可以被多個線程訪問是有意義的,但我理解你的觀點。每次只會創建一個新的「對象」?雖然我還在學習,但似乎新對象與'List'沒有任何關係。 – 2014-09-29 12:13:14

+2

在任何情況下,對象與其監視器之間都沒有關係 - 它只是另一個監視器。兩者之間的唯一聯繫是允許將方法標記爲「同步」的有些尷尬的「便利」語法。這個功能實際上比方便造成了更多的困惑。 – 2014-09-29 12:15:30