代码之家  ›  专栏  ›  技术社区  ›  Adam

共享资源能否安全地用作同步块的锁?

  •  0
  • Adam  · 技术社区  · 14 年前

    我经常发现自己的代码就像

    private static final MyType sharedResource = MyType();
    private static final Object lock = new Object();
    ...
    synchronized(lock)
    {
        //do stuff with sharedResource
    }
    

    这真的是必要的还是可以将SharedResource用作锁本身

    private static final MyType sharedResource = MyType();
    ...
    synchronized(sharedResource)
    {
        //do stuff with sharedResource
    }
    

    注意:示例中显示的同步块将位于执行工作的方法中,而不是方法本身或同步方法。

    编辑: 在一些答案中指出了一个非常好的观点,即如果我们处理多个共享资源,那么第一个“对象”技术要安全得多。

    警告 共享资源是 static 很重要!如果是 静止的 然后同步方法或同步块锁定 this 不起作用。锁定对象也必须是 静止的 .

    5 回复  |  直到 14 年前
        1
  •  1
  •   corsiKa    14 年前

    两者的利弊

    第一选择优点:允许锁定概念,而不是对象。如果您必须为一个动作锁定多个资源(通常不建议这样做,但有时是必要的),那么您可以这样做,而不用太担心种族状况。

    缺点:对象仍然可以修改,因此您需要确保对对象的访问被限制在遵守外部锁的方法中。

    第二个选项的优点:锁定对象应该防止其他人修改它(尽管你应该仔细检查确切的符号)。编辑:与上面相同的配置-如果方法不是 synchronized 在内部,您仍然可能遇到不遵守合同的方法。

    缺点:您阻止了对所有方法的访问,甚至那些与您试图操作的内容无关的方法,这可能会导致速度变慢,并可能导致死锁。但是,您可以很容易地得出这样的结论:如果是这样的话,您的对象正在做太多的工作,应该被分解。

    编辑:请允许我在此澄清第2部分(将mytype分解为myfoo,mybar将公开讨论…)

    class MyType {
       Foo foo;
       Bar bar;
    
       void doFoo() { foo.do(); }
       void doBar() { bar.do(); }
    }
    
    class MyActions {
        MyType thing;
    
        void lotsOfFoo() {
            // blocks bar :-(
            synchronized(thing) { thing.doFoo(); } 
        }
    
        void lotsOfBar() {
            // blocks foo :-(
            synchronized(thing) { thing.doBar(); } 
        }
    
    }
    

    就个人而言,我更经常使用选项1(这就是为什么我不确定选项2中的这一部分)。

        2
  •  1
  •   Janick Bernet    14 年前

    我看到的唯一问题是,使用 sharedResource 作为锁如果 MyType 方法定义为 synchronized 本身。在这种情况下,我所发生的一些奇怪的行为并不是 MyType . (有关示例,请参见Glowcoder的注释。)

    否则应该可以,但是如果您仍然只需要一个锁,那么只需在 this 而不是引入一个额外的虚拟对象。

    顺便说一句,你是故意让你 lock 对象静态?因为这意味着所有实例都锁定在同一个监视器上,也就是说,它们可以互相阻塞,这可能与您想要的行为不符。

        3
  •  0
  •   Drew Wills    14 年前

    是的,你完全可以做到。

    事实上,它提高了清晰度,减少了杂波。

        4
  •  0
  •   Viele    14 年前

    如果我记得对的话,synchronized是一个监视器,它允许在同一个JVM中的任何给定时间只有一个线程可以访问该对象。所以我认为您只访问shardresouce,在shardresouce上同步就足够了。

        5
  •  0
  •   gMale    14 年前

    就个人而言,我通常不会在任意锁上同步。也就是说,我通常会按照你的第二种方法做一些事情:

    private static final MyType sharedResource = MyType();
    ...
    synchronized(sharedResource) {
        //do stuff with sharedResource
    }
    

    当然,在运行同步代码之前,必须获取目标对象的锁。可以说,通常情况下,我会把锁“放下”一步。也就是说,我将在“SharedResource”中的方法上进行同步,如:

    public class MyType {
    
        public void synchronized doWork() {
    
        }
    }
    

    但是,当涉及到这类事情时,很难进行归纳,因为每个规则都有例外。最后,您的总体架构需求将决定应该在哪里进行锁定。对我来说,最常见的情况是,我发现自己正在同步访问共享资源的顶级方法,因此,锁定一个只做锁的对象更为罕见。

    编辑:小语法修复