тупик при синхронизации (String intern ()) - PullRequest
6 голосов
/ 08 декабря 2008

Я пользователь sun jdk 1.5 ThreadPoolExecutor (24, 24,60, TimeUnit.SECONDS, новый LinkedBlockingQueue ()). Так вот, я использую инструмент jdb, чтобы найти состояние всех потоков в пуле потоков, «ожидающих на мониторе», код:

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

Есть ли проблема в "synchronized (key.intern ())"?


Я получаю следующую информацию, используя инструмент jdb, 24 потока находятся в состоянии ожидания на мониторе, это означает, что 24 потока зашли в тупик при key.intern ().

(java.lang.Thread) 0x28 пул-3-нить-2, ожидающий в мониторе

(java.lang.Thread) 0x27 пул-3-нить-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 () может быть тупиковым, хорошо?

Ответы [ 11 ]

5 голосов
/ 08 декабря 2008

Я однажды опубликовал связанный с этим вопрос, на который вы, возможно, захотите взглянуть: Проблема с синхронизацией на объектах String?

То, что я узнал, было: использование интернированных строк для синхронизации - это плохая практика.

4 голосов
/ 08 декабря 2008

Довольно. Проблема в том, что key.intern () на самом деле не настолько уникален, поскольку возвращает строку из пула. String.intern () может возвращать один и тот же объект, даже если он используется для разных объектов. Попробуйте использовать key сам или совсем другой объект.

2 голосов
/ 08 декабря 2008

Недостаточно кода здесь, чтобы сказать, что происходит не так. Как уже упоминалось, это может быть узким местом, но для этого должен быть запущен хотя бы один поток (с довольно большой загрузкой ЦП), или поток, имеющий блокировку, переводится в спящий режим без снятия блокировки.

Взаимная блокировка - это еще одна возможность, но для этого потребуется синхронизация для двух отдельных блокировок в нескольких потоках, и вы показали здесь только один объект блокировки.

Это действительно невозможно определить без дополнительной информации.

2 голосов
/ 08 декабря 2008

У вас две проблемы. Один использует String в качестве блокировки. Второй тупик.

Если вы используете String в качестве блокировки, вы потеряете контроль над «кто» и «где» возьмут блокировку этого объекта.

Ваша проблема взаимоблокировки, которая может или не может быть вызвана блокировкой на строке. Однако настоящая причина тупика: «Ваш код может привести к тупику». Если это может произойти, это произойдет.

Вы должны отслеживать стеки ваших потоков, чтобы устранить тупики.

2 голосов
/ 08 декабря 2008

Если вам нужно синхронизировать строку, не используйте экземпляр String в качестве мьютекса (интернированный или нет). Для создания хорошего объекта мьютекса можно использовать строку: синхронизация по идентификатору .

2 голосов
/ 08 декабря 2008

Код почти наверняка пытается синхронизировать действия, которые влияют на один и тот же ключ. Поэтому он вызывает intern (), чтобы убедиться, что один и тот же ключ сопоставлен с одним и тем же объектом и, следовательно, является допустимым в качестве объекта для синхронизации.

Проблема, если вы сталкиваетесь с узким местом (это не тупик) в том, что у вас слишком много операций, приходящих одновременно с использованием одного и того же ключа.

Переосмыслите, что нужно синхронизировать.

1 голос
/ 23 августа 2017

Как насчет использования уникального префикса строки со значением блокировки и использования String.intern () в синхронизированном блоке. Например, если вы хотите заблокировать строку «lock1», используйте префикс UUID, например: «85e565b3-d440-46e7-93b6-69ee7e9a63ee-lock1». Этот тип строки не должен быть уже во внутреннем пуле. т. е. вероятность взаимной блокировки другого кода очень мала.

1 голос
/ 11 декабря 2008

Скорее всего, у вас тупик.

Если вы хотите избежать взаимоблокировок, каждый поток должен всегда получать блокировки в одном и том же порядке. Когда вы используете 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.");

Приведенный выше код продемонстрирует, что, хотя вы создали два уникальных экземпляра, интернируя их, вы заменили их одним каноническим экземпляром.

Существует опасность, когда вы используете объект, который виден за пределами вашего собственного кода, в качестве блокировки. Старайтесь придерживаться закрытых членов, объектов, которые вы не можете покинуть из своего стека и т. Д.

1 голос
/ 08 декабря 2008

Как говорит Бомбе, key.intern () не обязательно даст вам уникальный ключ для синхронизации.

Однако вы должны быть осторожны при изменении кода. Вам нужно понять стратегию блокировки в коде, прежде чем менять ее. Удаление вызова intern () может дать вам код, который, кажется, работает правильно, но содержит гонку данных, которая укусит вас позже.

0 голосов
/ 08 декабря 2008

String.intern () - это собственный метод, который может быть причиной проблемы.

...