【问题标题】:deadlock on synchronized ( String intern())同步死锁(字符串实习生())
【发布时间】:2010-09-25 19:50:31
【问题描述】:

我用户 sun jdk 1.5 ThreadPoolExecutor(24, 24,60,TimeUnit.SECONDS, new LinkedBlockingQueue())。 soemtime我使用jdb工具查找线程池中所有线程的状态都是“在监视器中等待”,代码是:

    String key = getKey(dt.getPrefix(), id);
    synchronized (key.intern()) {      ----->

“同步的(key.intern())”有问题吗?


我使用 jdb 工具得到以下信息,24 个线程的状态是“在监视器中等待”,这意味着 24 个线程在“key.intern()”处死锁。

(java.lang.Thread)0x28 pool-3-thread-2 在监视器中等待

(java.lang.Thread)0x27 pool-3-thread-3 在监视器中等待

(java.lang.Thread)0x1b pool-3-thread-4 在监视器中等待

(java.lang.Thread)0x1a pool-3-thread-5 在监视器中等待

(java.lang.Thread)0x19 pool-3-thread-6 在监视器中等待

(java.lang.Thread)0x18 pool-3-thread-7 在监视器中等待

(java.lang.Thread)0x17 pool-3-thread-8 在监视器中等待 ...

所以结果是:在多线程环境下,Sting intern() 方法可能会死锁,好吗?

【问题讨论】:

  • 第一个问题是你想达到什么目标?问题是什么?为什么需要同步?
  • 您是否查看过线程池外的线程以查看它们是否也在监视器中等待?您还应该注意每个线程同步的确切对象。
  • 线程池外有两个线程在一个monitor上等待,同时也在监听同一行(key.intern())。
  • 在实习字符串中同步根本不起作用。它不保证在不同的线程中返回相同的字符串对象,因为与实习生操作没有“发生在之前”的关系。因此,这两个线程具有不同的实习字符串表,直到它们通过同一对象上的监视器(或以其他方式建立“发生在之前”) identityHashCode() 但相同的字符串值......它发生了大约 1/3 的时间(在这种情况下)。

标签: java string deadlock synchronized string-interning


【解决方案1】:

我曾经发布过一个与此相关的问题,您可能想看看:Problem with synchronizing on String objects?

我学到的是:使用实习字符串进行同步是一种不好的做法。

【讨论】:

  • 如果您详细说明为什么这是一种不好的做法,那将会很有帮助。
  • 链接的问题没有回答吗? 在针对 webapp 运行 JMeter 测试以模拟预期负载时,我看到使用的堆大小在不到 20 分钟内增长到近 1GB。
  • 如果您在答案中快速解释原因而不是让人们阅读冗长的问题,那将是一个更好的答案。
【解决方案2】:

相当。问题是 key.intern() 并不是唯一的,因为它从池中返回一个字符串。即使在不同的对象上使用 String.intern() 也可能返回相同的对象。尝试使用 key 本身或完全不同的对象。

【讨论】:

    【解决方案3】:

    代码几乎肯定会尝试同步影响同一键的操作。所以它调用 intern() 来确保相同的键被映射到相同的对象,因此作为同步对象是有效的。

    问题是,如果您在那里遇到瓶颈(这不是死锁),是因为您有太多使用同一个键同时进入的操作。

    重新考虑需要同步的内容。

    【讨论】:

    • 你怎么知道他说的是瓶颈?
    • 你怎么知道不是死锁。使用诸如实习字符串之类的通用可访问的东西是陷入僵局的一种简单方法。
    • 死锁的定义是两个(或更多)线程正在等待其他线程拥有的资源。这意味着两个同步块(或一个循环)。显示的代码虽然可能在循环中,但似乎是一个非常简单的临界区门。
    【解决方案4】:

    如果您需要在 String 上进行同步,请不要使用 String 实例作为互斥体(无论是否为 interned)。不过,字符串可用于创建良好的互斥对象:synchronizing on an ID

    【讨论】:

      【解决方案5】:

      你有两个问题。 一个是使用字符串作为锁。第二个是死锁。

      如果你使用字符串作为锁,你将失去对“谁”的控制,“哪里”将获得该对象锁。

      您的死锁问题,可能是由 String 上的锁定引起的,也可能不是。然而,死锁的真正原因是:“你的代码会导致死锁。”。如果它可以发生,它就会发生。

      您必须跟踪线程的堆栈以解决死锁。

      【讨论】:

      • 使用 String.intern() 作为锁有什么问题?我自己用过成功
      • 当然可以使用字符串作为锁。但是,正是由于答案中提到的原因,这样做很少是“好”(无论这意味着什么)。
      • 不确定我是否理解它的坏处。那就是导致死锁?或者它是一个瓶颈?我认为两者都不是真的。
      • 重点是完全不相关的代码可以占用“你的”锁,因为它恰好也使用了 String.intern()——这导致同一个对象被用作锁。
      【解决方案6】:

      这里没有足够的代码来说明出了什么问题。如前所述,这可能是一个瓶颈,但至少应该有一个线程正在运行(CPU 使用率相当高)才能发生这种情况,或者有锁的线程在不释放锁的情况下进入睡眠状态。

      死锁是另一种可能性,但这需要在多个线程上的两个独立锁上进行同步,并且您在此处仅显示了一个锁对象。

      如果没有更多信息,确实无法确定。

      【讨论】:

        【解决方案7】:

        正如 Bombe 所说,key.intern() 不一定会给你一个非常独特的同步密钥。

        但是,您应该谨慎更改代码。在更改代码之前,您需要了解代码中的锁定策略。删除 intern() 调用可能会为您提供看似正常工作但包含稍后会咬您的数据竞争的代码。

        【讨论】:

          【解决方案8】:

          你很可能遇到了死锁。

          如果你想避免死锁,每个线程必须始终以相同的顺序获取锁。当您使用 String.intern() 获取锁时,您锁定的是整个 JVM 中的任何代码都可以访问并锁定的实例。很可能,您自己代码中的其他线程处于死锁状态,但并非必须如此。

          我不确定“key.intern() 保证唯一性”在您的回答中是什么意思。 intern() 方法通过为每个等效的字符串返回相同的对象降低唯一性。

            String s1 = new String(new char[] { 'c', 'o', 'm', 'm', 'o', 'n' }).intern();
            String s2 = new String("commo" + (s1.charAt(s1.length() - 1)).intern();
            String s3 = "common";
            if ((s1 == s2) && (s1 == s3))
              System.out.println("There's only one object here.");
          

          上面的代码将证明,即使您创建了两个唯一的实例,通过实习它们,您也可以将它们替换为一个规范的实例。

          任何时候您使用在您自己的代码之外可见的对象作为锁都有危险。尝试使用私有成员、不允许从自己的堆栈中逃逸的对象等。

          【讨论】:

            【解决方案9】:

            如何使用具有锁定值的唯一字符串前缀并在同步块中使用 String.intern()。例如,如果您想锁定字符串“lock1”,请使用这样的 UUID 前缀:“85e565b3-d440-46e7-93b6-69ee7e9a63ee-lock1”。这种类型的字符串不应该已经在实习生池中。即其他代码死锁的可能性非常低。

            【讨论】:

              【解决方案10】:

              key.intern() 保证唯一性,因为 key.intern() 从字符串常量池返回一个字符串。

              http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html#intern() 实习生

              公共字符串实习生() 返回字符串对象的规范表示。 一个字符串池,最初是空的,由 String 类私下维护。

              【讨论】:

                【解决方案11】:

                String.intern() 是本机方法 - 这可能是导致问题的原因。

                【讨论】:

                • 为什么会导致问题?我认为该方法是否为本机这一事实没有影响,它发生在同步开始之前。
                猜你喜欢
                • 2011-10-22
                • 2012-01-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-02-11
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多