2010-08-11 65 views
2

我会尝试通过以下三种情况来解释这个问题。 案例一: 我是用使用这样的同步共享锁:使用本地锁而不是共享锁进行同步安全吗?


     private static final String SHARED_LOCK = "shared_lock"; 
     private static int i = 0; 
     private static int j = 0; 
    void increment() { 
     synchronized (SHARED_LOCK) { 
      i++; 
      j++; 
     } 
    } 

而且这是工作的罚款。

案例二: 现在我已经在这改变是不是使用共享锁,我做这样的事情想使用本地锁:


     private static int i = 0; 
     private static int j = 0; 
    void increment() { 
     final String LOCAL_LOCK = "local_lock"; 
        synchronized (LOCAL_LOCK) { 
      i++; 
      j++; 
     } 
    } 

而且我发现代码仍然是工作罚款是同步仍在工作。

案例三: 然而,当我改变了当地LOCL这样:

final String LOCAL_LOCK = new String("local_lock");

然后同步走了。所以看起来在CASE II中,本地锁能够提供同步,因为Java会为我们自动执行String实例,但是在CASE III中,我每次都明确地创建一个新的String,因此没有发生同步。

所以回到我原来的问题。有没有人认为CASE II不是实现同步的正确方法?如果是的话,请你也提一下为什么?

在此先感谢, SacTiw。

回答

3

什么是非常非常重要的这里是用“canonicalizable”对象 - 公司,比如,String s,这可以被拘留,或Integer常量可以共享的 - 基本上是开放您使用相同的锁定对象作为另一个类。

正如你想象的那样,这是一个非常糟糕的主意。通过使用与另一个类相同的锁,您可以在以后尝试并确定应用程序死锁的原因时,基本上可以确保自己处于一个痛苦调试的整个世界。

如果您需要课堂上的私人锁定,请确保它是完全唯一的对象,只能通过new运算符获取。

(我相信Java的谜题演讲可能已经覆盖了这一之一;其他一些例子引用herehere

+0

所以在这里你的意思是情况2可能会导致一些其他线程访问其他类可能会开始使用相同的锁作为这个类,这将导致性能差和痛苦的调试。对? – sactiw 2010-08-12 09:57:12

+0

在Java语言规范中提到,在Java中,不同包中不同类中的文字字符串(不可变)同样代表对同一个字符串对象的引用。这个规则适用于所有原始对象,因为它们都是不可变的。 因此,这意味着如果我们按情况2行事,可能导致性能下降,并在面临死锁时进行痛苦的调试。 所以我认为我可以说,我的问题可以通过案例2进行同步,但它并不是所有推荐的使用方式。 – sactiw 2010-08-12 14:24:19

+0

绝对,sactiw。使用除私有的,不可访问的其他类对象以外的任何东西通常是一个非常糟糕的主意。如果你通过'new'得到一个对象,你就知道它是你自己的(只要你不泄漏它)。 – Cowan 2010-08-12 21:04:35

8

锁是为了在单独的线程中运行的代码之间提供同步。如果你使用本地锁,每个线程将有自己的锁对象副本(在线程的堆栈中),它将锁定它,因此不会有同步。

它只适用于你的情况,因为字符串interning。如果您使用final Object LOCAL_LOCK = new Object();则无法使用。

因此锁应始终共享线程之间。

+0

+1实习。我们期望每次调用'increment()'都有自己的锁,因为它是一个局部变量。在这种情况下使用'String'是特殊的(导致同步意外工作),因为VM为所有变量使用相同的'String'实例,其中具有相同的文本。有关详细信息,请参阅http://en.wikipedia.org/wiki/String_interning。 – f1sh 2010-08-11 10:23:14

+0

我知道为什么case-II能正常工作。我已经提到了在我的问题本身中由Java完成的String自动实习。我也知道为什么case-III失败,因为每个线程都有自己的锁版本,所以他们等待其他线程完成。所以我真正感兴趣的是知道是否使用案例II进行同步是非常安全的,还是存在后一种可能出现的案例II的一些隐藏问题。 – sactiw 2010-08-12 09:52:26

+0

@sactiw:如果你总是使用interned字符串作为锁,那么它可能就没问题。但是这仍然是一种不好的做法,因为有一天有人可能会将字符串更改为任何其他类型,并且一切都会神秘失败。 – 2010-08-12 10:09:06

1

这是因为字符串实习。如情况2中创建的所有字符串具有相同的值将“共享”相同的实例。如情况3中创建的字符串将是不同的实例。

正是这种差异说明了您所看到的不同步行为。

4

只有在线程之间的同一对象上同步时,同步才能正确工作;

  • 第一种情况下是显而易见的,
  • 在第二种情况下,Java编译器实习生字符串常量这就是为什么它也能工作(你从电话之间的串池相同的对象),
  • 的第三种情况你强制一个新的字符串对象,因此你得到你自己的私人锁定每个线程根本不同步。