7

我用户 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() 方法可能会死锁,好吗?

4

11 回答 11

5

我曾经发布过一个相关的问题,您可能想看看:String objects 的同步问题?

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

于 2008-12-08T14:54:59.710 回答
4

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

于 2008-12-08T09:28:49.977 回答
2

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

问题是,如果您在那里遇到瓶颈(这不是死锁),那么您有太多操作同时使用相同的密钥进入。

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

于 2008-12-08T12:52:37.070 回答
2

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

于 2008-12-08T13:07:31.923 回答
2

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

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

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

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

于 2008-12-08T13:18:58.343 回答
2

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

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

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

于 2008-12-08T15:03:35.763 回答
1

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

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

于 2008-12-08T11:26:00.583 回答
1

你很可能陷入僵局。

如果要避免死锁,每个线程必须始终以相同的顺序获取锁。当您使用 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.");

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

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

于 2008-12-11T05:34:27.500 回答
1

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

于 2017-08-23T04:09:03.787 回答
-1

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

于 2008-12-08T11:09:47.653 回答
-1

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

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

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

于 2008-12-08T09:43:48.263 回答