【问题标题】:How does Java Synchronized compare locked objects?Java Synchronized 如何比较锁定的对象?
【发布时间】:2013-05-21 16:31:39
【问题描述】:

我想在多个 API 请求上锁定一个对象,这样每个用户只有一个请求可以输入一段代码。

synchronized(obj) 是否基于对象的引用或其hashCode() 函数锁定?

即我能做吗:

synchronized("asdf") {
    doSomethingNifty();
}

这里的“asdf”有一个唯一的哈希值,但没有唯一的引用。

【问题讨论】:

  • 在对象上同步。
  • "asdf" 实际上有一个唯一的引用:它是一个字符串文字,所有字符串文字都引用同一个 String 实例。

标签: java multithreading synchronization


【解决方案1】:

synchronized(obj) 是否基于对象的内存位置或其 toHash() 函数进行锁定?

两者都不是。它锁定在与对象关联的监视器上。就 JVM 而言,我们不谈论对象的内存地址,因为它是可重定位的,而且它不是哈希码(即使是 Object.hashcode()),因为它不是唯一的。

就您应该锁定的内容而言,它应该是相同的final 对象。比如:

private final Object lockObject = new Object();
...
synchronized (lockObject) {
   // do stuff that needed to be protected
}

你希望它是final,这样可以保证多个线程锁定在同一个不改变的对象引用上。 private 很好,所以外面的班级不能搞砸班级内部的锁定。

这里的“asdf”有一个唯一的哈希,但没有唯一的内存地址。

"asdf" not 有唯一的散列,因为其他字符串可能具有相同的散列,实际上 可能 在所有使用 " 时都有唯一的"内存地址"如果编译器将其存储在 Java string pool 中,则将其存储在您的应用程序中。这意味着某些完全不同的类也可能具有相同的错误模式代码块,并且会影响您的类的同步,因为它将锁定在同一个 String 对象实例上。这就是为什么private 锁定对象如此重要的原因。

当我们讨论这个主题时,您也绝不能同步可变值,例如 BooleanInteger 等非最终对象。以下模式经常使用,而且非常错误:

Boolean value = false;
...
// really bad idea
synchronized (value) {
   if (value) {
      value = false;
   } else {
      value = true;
   }
}

这是非常错误的,因为 value 引用正在更改。因此,一个线程可能会锁定它,然后更改它的参考值,以便另一个线程将锁定另一个对象,并且两个线程将同时位于 synchronized 内。更糟糕的是,Boolean 只有两个 truefalse 值是常量,因此多个类将锁定相同​​的引用。

【讨论】:

    【解决方案2】:

    您正在对一个对象进行同步,内存地址问题纯粹是一个实现问题,与您无关。只要是同一个对象(即完全相同的实例),同步就完成了。

    如果您使用不同的实例,同步将不起作用。您可以做的是定义一个公共静态常量作为锁:

    public final static Object LOCK = new Object();
    

    并使用它

    synchronized(SomeClass.LOCK) {
    

    【讨论】:

    • 为什么是String 而不是Object
    • 在字符串文字值上同步是不好的做法,并且可能导致错误。字符串文字“asdf”作为字符串常量出现在字节码中,并且在类加载时是“实习”的。如果两个类使用相同的字符串执行此操作,则两个字段都引用相同的内部字符串。这意味着您只有一个锁对象,而您认为您有两个。因此,您可能会看到不正确的行为,甚至是死锁。
    【解决方案3】:

    锁在对象本身的实例上,如果你想这样想的话,你可以把它看作内存位置。所以你的建议是行不通的。

    相反,听起来您只需要一个与您要保护的代码块相对应的对象。因此,在您的程序设置过程中的某个地方,您需要类似

    static Object lockObject = new Object();
    

    然后你就可以了

    synchronized(lockObject) {
        doSomethingNifty();
    }
    

    但是,如果 doSomethingNifty() 与特定对象相关,那么使用该对象而不是 lockObject 会很有意义。还要确保 doSomethingNifty() 快速执行,否则会有很多用户等待。

    【讨论】:

    • lockObject 应该比final 多于static
    • @Gray 是的,当然:-),但我几乎总是发现对于lockObject 所在的任何类的所有实例,我只需要一个lockObject。如果是这样,那么static 确实是必需的。我同意 final 很好,非常准确等,但它只是防止 lockObject 的意外更改,如果它是仅用于同步目的的单一用途对象,则不太可能发生编程错误。
    • 明白。仅供参考:final 在围绕对象初始化的多线程程序中还有许多其他好处。
    猜你喜欢
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2012-04-27
    • 2016-10-02
    相关资源
    最近更新 更多