使用本地锁而不是共享锁进行同步是否安全?

时间:2010-08-11 09:05:22

标签: java synchronization

我将尝试通过以下3个案例来解释这个问题。 案例I: 我使用共享锁进行同步,使用类似的东西:


         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++;
        }
    }

它工作正常。

案例II: 现在我在这里更改的是使用共享锁而不是使用本地锁来执行这样的操作:


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

我发现代码仍然正常工作,同步仍然有效。

案例III: 但是当我将本地locl更改为:

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

然后同步就消失了。因此看起来在CASE II中,本地锁能够提供同步,因为Java会自动为我们执行的字符串文字的实现,但在CASE III中,因为我每次都显式创建一个新的字符串,因此没有发生同步。

回到我原来的问题。有谁觉得CASE II不是实现同步的正确方法吗?如果是,请你也提一下为什么?

提前致谢, SacTiw。

4 个答案:

答案 0 :(得分:8)

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

它仅适用于您的情况,因为String interning。如果您使用final Object LOCAL_LOCK = new Object();则无效。

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

答案 1 :(得分:4)

只有在线程之间同步对象时,同步才有效;

  • 第一种情况很明显,
  • 在第二种情况下,Java编译器实现字符串常量,这也是它工作的原因(在调用之间从字符串池中获取相同的对象),
  • 第三种情况是强制使用一个新的字符串对象,因此每个线程都会获得自己的私有锁,根本不会同步。

答案 2 :(得分:3)

这里非常重要的是使用'canonicalizable'对象 - 比如说,String s,可以被实现,或Integer常量可以共享 - 基本上是打开你的最多使用与另一个类相同的锁对象。

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

如果您需要在类中使用私有锁,请通过new运算符获取它,确保它是一个完全唯一的对象。

(我相信其中一个Java Puzzlers演示文稿可能已经涵盖了这一点;其他一些示例参考文献是herehere

答案 3 :(得分:1)

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

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