Статьи

Синхронизация потоков Java и параллелизм, часть 1

Вступление

Синхронизация потоков Java и параллелизм являются наиболее обсуждаемыми темами на различных этапах проектирования сложного приложения. Есть много аспектов потоков, методов синхронизации для достижения большого параллелизма в приложении. Эволюция процессоров за последние годы (многоядерные процессоры, регистры, кэш-память и оперативная память (RAM)) привела к тому, что разработчики обычно не замечают таких областей, как контекст потока, переключение контекста, видимость переменных, память JVM модель против модели памяти процессора. 

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

рекапитуляция

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

  1. Lock — блокировка — это механизм синхронизации потоков.
  2. Каждый объект в Java имеет встроенную блокировку, связанную с ним. Потоки используют монитор объекта для блокировки или разблокировки. Блокировка может рассматриваться как данные, которые логически являются частью заголовка объекта в памяти. Посмотрите ReentrantLock для расширенных возможностей, которые монитор не может достигнуть.
  3. Каждый объект в Java имеет методы синхронизации wait()и notify()[также notifyAll()]. Любой поток, вызывающий эти методы, получает блокировку этого объекта, используя свой монитор. Это должно быть вызвано с использованием синхронизированного ключевого слова else, и IllegealMonitorStateException будет брошен.
  4. Сигнал является способом уведомить нить , что она должна продолжить ее выполнение. Это достигается с помощью методов объекта wait(), notify(), и notifyAll(). Вызов методов, notify()или notifyAll(), выбирает поток (ы), чтобы разбудить то, что находится в фоновом режиме (путем вызова метода, wait()).
  5. Пропущенный сигнал — Методы notify()и notifyAll()не спасают вызовы методов, и они не знают , если wait()были названы или не другими потоками. Если поток вызывает notify()до того, как будет вызван поток wait(), сигнал которого будет пропущен ожидающим потоком. Это может заставить поток ждать бесконечно, потому что он пропустил сигнал.
  6. Runnable это функциональный интерфейс, который может быть реализован любым классом в приложении, чтобы поток мог его выполнить.
  7. volatileдругое ключевое слово, назначаемое переменным, чтобы сделать классы потокобезопасными. Чтобы понять использование этого ключевого слова, необходимо понять архитектуру процессора и модель памяти JVM. Мы рассмотрим это позже. 
  8. ThreadLocalпозволяет создавать переменные, которые могут быть прочитаны / записаны только потоком владельца. Это используется, чтобы сделать код потокобезопасным.
  9. Пул потоков — это набор потоков, в которых потоки будут выполнять задачи. Создание и поддержка потоков очень контролируется сервисом. В Java пул потоков представлен экземпляром ExecutorService .
  10. ThreadGroup Этот класс предоставляет механизм для сбора нескольких потоков в один объект и позволяет нам манипулировать / контролировать эти потоки одновременно.
  11. Поток демона — эти потоки работают в фоновом режиме. Хорошим примером потока демона является сборщик мусора Java. JVM не ждет, пока поток демона не завершит свое выполнение (в то время как JVM ожидает, когда потоки, не являющиеся демонами, или пользовательские потоки завершат свое выполнение).
  12. синхронизированный — ключевое слово для управления выполнением кода одним потоком, когда различные потоки должны выполнять один и тот же фрагмент функциональности в параллельном режиме. Это ключевое слово может применяться для методов и блоков кода для достижения безопасности потока. Обратите внимание, что для этого ключевого слова нет тайм-аута, поэтому существует вероятность возникновения тупиковых ситуаций.
  13. Dead-lock — ситуация, когда один или несколько потоков ожидают снятия блокировки объекта другим потоком. Возможный сценарий, который вызывает мертвые блокировки, может быть в том случае, когда потоки ждут друг друга, чтобы снять блокировку!
  14. Ложные пробуждения — По необъяснимым причинам потоки могут проснуться, даже если notify()и notifyAll()не были вызваны. Это ложное пробуждение. Чтобы покрыть эту проблему, поток пробуждал вращения вокруг условия в блокировке вращения.

Джава