Советы по предотвращению взаимоблокировок в java

Я изучаю потоки java и взаимоблокировки, я понимаю примеры взаимоблокировок, но мне интересно, есть ли общие правила для предотвращения этого.

Мой вопрос в том, есть ли правила или советы, которые могут быть применены к исходному коду в java для предотвращения взаимоблокировок? Если да, не могли бы вы объяснить, как его реализовать?

10 ответов

Несколько быстрых советов из головы

  • не использовать несколько потоков (например, Swing делает, например, путем указания того, что все сделано в EDT)
  • не удерживать сразу несколько замков. Если вы это сделаете, всегда приобретайте блокировки в том же порядке
  • не выполнять внешний код при удерживании блокировки
  • использовать прерывистые блокировки


Инкапсулировать, инкапсулировать, инкапсулировать! Вероятно, самая опасная ошибка, которую вы можете сделать с помощью блокировок, - это разоблачение вашей блокировки в мире (что делает ее общедоступной). Невозможно сказать, что может произойти, если вы сделаете это, так как любой сможет получить блокировку без знания объекта (поэтому вы не должны блокировать this). Если вы оставите свою блокировку конфиденциальной, тогда у вас есть полный контроль, и это делает ее более управляемой.


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


Прочитайте и поймите Java: Concurrency и Practice. Речь идет не о "советах", чтобы избежать тупиковой ситуации. Я бы никогда не нанял разработчика, который знал несколько советов, чтобы избежать тупика и часто избегал тупика. Это о понимании concurrency. К счастью, существует всеобъемлющая книга промежуточного уровня по этой теме, поэтому читайте ее.


  • Не используйте блокировки.
  • Если вам нужно, держите свои локальные блоки. Глобальные блокировки могут быть очень сложными.
  • Сделайте как можно меньше, удерживая замок.
  • Используйте stripes только для блокировки сегментов ваших данных.
  • Предпочитают Неизменяемые типы. Много раз это означает копирование данных вместо совместного использования данных.
  • Вместо этого используйте сравнение и набор (CAS), см. AtomicReference.


Учитывая выбор дизайна, используйте передачу сообщений, где только блокировки находятся в очереди push/pop. Это не всегда возможно, но если это так, у вас будет очень мало тупиков. Вы все равно можете их получить, но вам нужно очень стараться:)


Существует почти одно правило, когда дело доходит до предотвращения взаимоблокировок:

Если вам нужно иметь несколько блокировок в коде, убедитесь, что все они всегда приобретают их в том же порядке.

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


Это классический пример тупика:

public void methodA(){
 synchronized(lockA){
 //...
 synchronized(lockB){
 //...
 }
 }
}
public void methodB(){
 synchronized(lockB){
 //...
 synchronized(lockA){
 //...
 }
 }
}

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


  • Избегайте вложенных блокировок. Это самая распространенная причина тупика. Избегайте блокировки другого ресурса, если у вас уже есть один. Почти невозможно получить тупик, если вы работаете только с одним блокиром объекта.

  • Заблокируйте только то, что требуется. Как блокировать конкретное поле объекта, а не блокировать весь объект, если он служит вашей цели.

  • Не ждите бесконечно.


  • Если не требуется, не обменивайтесь данными по нескольким потокам. Если данные не могут быть изменены после создания/инициализации, придерживайтесь конечных переменных.
  • Если вы не можете избежать совместного использования данных между несколькими потоками, используйте гранулированные блоки synchronized или Lock s.
  • Если вы используете только блоки кода synchronized, убедитесь, что блокировки получены/выпущены в определенном порядке.
  • Посмотрите на другие альтернативы: volatile или AtomicXXX или Lock API

Связанные вопросы SE:

Избежать синхронизации (это) в Java?

Разница между изменчивой и синхронизированной в Java

Volatile boolean vs AtomicBoolean

licensed under cc by-sa 3.0 with attribution.