2014-09-29 73 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