-
Поскольку несколько потоков одного и того же процесса используют одно и то же пространство памяти, что обеспечивает удобство, но также приводит к серьезной проблеме конфликтов доступа. Язык Java предоставляет специальный механизм для разрешения этого конфликта, эффективно предотвращая одновременный доступ к одному и тому же объекту данных несколькими потоками.
Поскольку мы можем использовать ключевое слово Private, чтобы гарантировать, что доступ к объекту данных будет возможен только с помощью методов, нам нужно всего лишь предложить механизм для методов. Этот механизм представляет собой ключевое слово Synchronized, которое включает два варианта использования: синхронизированный метод и синхронизированный блок.
1. синхронизированный метод: объявите синхронизированный метод, добавив ключевое слово Synchronized в объявление метода. нравиться:
общедоступная синхронизированная пустота accessVal (int newVal);
Синхронизированный метод контролирует доступ к переменным-членам класса: каждый экземпляр класса соответствует блокировке. Каждый синхронизированный метод должен получить блокировку экземпляра класса, который вызывает метод, прежде чем он сможет быть выполнен. В противном случае поток, которому он принадлежит, блокируется. После выполнения метода он будет занимать исключительно переменную-член класса. Блокировка не будет снята до возврата из этого метода. После этого заблокированный поток может получить блокировку и повторно войти в состояние исполняемого файла. Этот механизм гарантирует, что для каждого экземпляра класса одновременно не более одной из всех его функций-членов, объявленных как синхронизированные, находится в исполняемом состоянии (поскольку максимум можно получить блокировку, соответствующую экземпляру класса) (обратите внимание на этот оператор ! Потому что это блокировка для экземпляра класса, поэтому для объекта одновременно выполняется только один синхронизированный метод в одном потоке), что позволяет эффективно избегать конфликтов доступа к переменным-членам класса (при условии, что все методы, которые могут переменные-члены класса доступа объявляются как синхронизированные).
В Java не только экземпляры классов, каждый класс также соответствует блокировке. Таким образом, мы также можем объявить статические функции-члены класса как синхронизированные, чтобы контролировать доступ к статическим переменным-членам класса.
Дефекты синхронизированного метода: если большой метод объявлен как синхронизированный, это сильно повлияет на эффективность. Обычно, если метод класса потока run() объявлен как синхронизированный, он будет продолжать выполняться в течение всего жизненного цикла потока. . Приведет к тому, что вызовы любых синхронизированных методов этого класса никогда не будут успешными. Конечно, мы можем решить эту проблему, поместив код, который обращается к переменным-членам класса, в специальный метод, объявив его как синхронизированный и вызвав его в основном методе, но Java предлагает нам лучшее решение — синхронизированный блок.
2. синхронизированный блок: объявите синхронизированный блок с помощью ключевого слова Synchronized. Синтаксис следующий:
синхронизированный (syncObject) {
//Код, разрешающий контроль доступа
}
Синхронизированный блок — это блок кода, в котором код должен получить блокировку объекта syncObject (как упоминалось ранее, это может быть экземпляр класса или класс), прежде чем он сможет быть выполнен. Конкретный механизм такой же, как упоминалось выше. Поскольку он может ориентироваться на любой блок кода и произвольно указывать заблокированный объект, он обладает высокой гибкостью.
Блокировка потока (синхронизация)
Для решения конфликтов доступа к общим областям хранения в Java введен механизм синхронизации. Теперь давайте рассмотрим доступ к общим ресурсам несколькими потоками. Очевидно, что механизма синхронизации уже недостаточно, поскольку требуемых ресурсов в любой момент может не хватить. В свою очередь, чтобы к нему можно было получить доступ, одновременно может быть готово более одного ресурса. Чтобы решить проблему контроля доступа в этом случае, в Java введена поддержка механизма блокировки.
Блокировка означает приостановку выполнения потока для ожидания выполнения определенного условия (например, готовности ресурса). Студенты, изучавшие операционные системы, должны быть с ним знакомы. Java предоставляет большое количество методов для поддержки блокировки. Давайте проанализируем их один за другим.
1. Метод Sleep(): Sleep() позволяет указать период времени в миллисекундах в качестве параметра. Он приводит к тому, что поток переходит в состояние блокировки в течение указанного времени и не может получить время ЦП. поток снова входит в состояние исполняемого файла.
Обычно функция Sleep() используется при ожидании готовности ресурса: после того, как тест обнаруживает, что условие не выполнено, поток блокируется на некоторое время, а затем повторяется тестирование до тех пор, пока условие не будет выполнено.
2. Методы suspend() и возобновить() (легко вызвать взаимоблокировку, устаревшие): эти два метода используются вместе. suspend() приводит к переходу потока в состояние блокировки и не требует автоматического восстановления. вызывается, может ли поток повторно войти в состояние исполняемого файла. Обычно suspend() и возобновить() используются при ожидании результата, выданного другим потоком: после того, как тест обнаруживает, что результат не был выдан, поток блокируется, а после того, как другой поток выдаст результат, вызовите возобновить(). чтобы возобновить его.
3. Метод yield(): метод yield() заставляет поток отдать выделенное в данный момент время ЦП, но не вызывает блокировку потока, то есть поток все еще находится в исполняемом состоянии, и ему может быть снова выделено время ЦП в в любое время. Эффект от вызова метода yield() эквивалентен тому, что планировщик считает, что поток выполнил достаточно времени, чтобы перейти к другому потоку.
4. Методы wait() и notify(): эти два метода используются вместе. wait() переводит поток в состояние блокировки. Он имеет две формы: одна позволяет указать период времени в миллисекундах в качестве параметра, а другая — в миллисекундах. другие параметры отсутствуют, первый повторно войдет в состояние исполняемого файла при вызове соответствующего notify() или превышении указанного времени, а второй должен быть вызван при вызове соответствующего notify().
На первый взгляд они ничем не отличаются от пар методов suspend() и возобновить(), но на самом деле они совершенно другие. Основное отличие состоит в том, что все описанные выше методы не освобождают занятую блокировку (если она занята) при блокировке, в то время как это противоположное правило является противоположным.
Вышеупомянутые основные различия приводят к ряду детальных различий.
Прежде всего, все описанные выше методы относятся к классу Thread, но эта пара принадлежит непосредственно классу Object, то есть эту пару методов имеют все объекты. На первый взгляд это может показаться невероятным, но на самом деле это очень естественно, потому что при блокировке этой пары методов занятая блокировка должна быть освобождена, а блокировка принадлежит любому объекту. Вызов метода wait() любого объекта вызывает ошибку. поток для блокировки, и блокировка объекта снимается. Вызов метода notify() любого объекта приведет к блокировке случайно выбранного потока путем вызова метода wait() разблокируемого объекта (но он не будет выполнен до тех пор, пока не будет получена блокировка).
Во-вторых, все описанные выше методы можно вызывать в любом месте, но эта пара методов (wait() и notify()) должна вызываться в синхронизированном методе или блоке. Причина тоже очень проста, только в синхронизированном методе. или блокировать Только текущий поток занимает блокировку, и блокировку можно снять. Точно так же блокировка объекта, вызывающего эту пару методов, должна принадлежать текущему потоку, чтобы блокировку можно было снять. Таким образом, пара вызовов методов должна быть помещена в синхронизированный метод или блок, заблокированным объектом которого является объект, вызывающий пару методов. Если это условие не выполнено, хотя программа все еще может компилироваться, во время выполнения возникнет исключение IllegalMonitorStateException.
Вышеуказанные характеристики методов wait() и notify() определяют, что они часто используются вместе с синхронизированными методами или блоками. Сравнение их с механизмом межпроцессного взаимодействия операционной системы выявит их сходство: синхронизированные методы или блоки обеспечивают сходство. к функциям примитивов операционной системы, их выполнение не будет мешать механизму многопоточности, и этот аналог эквивалентен примитивам блокировки и пробуждения (оба эта пара методов объявлены синхронизированными). Их комбинация позволяет нам реализовать в операционной системе ряд изысканных алгоритмов межпроцессного взаимодействия (например, семафорные алгоритмы) и может использоваться для решения различных сложных задач межпоточного взаимодействия.
Два последних замечания о методах wait() и notify():
Во-первых: разблокированный поток, вызванный вызовом метода notify(), выбирается случайным образом из потоков, заблокированных вызовом метода wait() объекта. Мы не можем предсказать, какой поток будет выбран, поэтому будьте особенно осторожны при программировании, чтобы избежать этого. проблемы, возникающие из-за этой неопределенности.
Второе: помимо notify() существует еще метод notifyAll(), который также может играть аналогичную роль. Единственное отличие состоит в том, что вызов метода notifyAll() удалит все потоки, заблокированные вызовом метода wait(). Объект сразу разблокирован. Конечно, только поток, получивший блокировку, может перейти в исполняемое состояние.
Говоря о блокировке, мы должны говорить о взаимоблокировке. Краткий анализ может показать, что как метод suspend(), так и вызов метода wait() без указания периода ожидания могут вызвать взаимоблокировку. К сожалению, Java не поддерживает предотвращение взаимоблокировок на уровне языка, и мы должны быть осторожны, чтобы избежать взаимоблокировок в программировании.
Выше мы проанализировали различные методы блокировки потоков в Java. Мы сосредоточились на методах wait() и notify(), поскольку они являются наиболее мощными и наиболее гибкими в использовании, но это также приводит к их снижению эффективности. более подвержен ошибкам. В реальном использовании мы должны гибко использовать различные методы для лучшего достижения наших целей.
демонический поток
Потоки демона — это особый тип потока. Разница между ними и обычными потоками заключается в том, что они не являются основной частью приложения. Когда все потоки приложения, не являющиеся демонами, завершаются, даже если потоки демона все еще работают, приложение. завершится, с другой стороны, приложение не завершится, пока работает поток, не являющийся демоном. Потоки демона обычно используются для предоставления услуг другим потокам в фоновом режиме.
Вы можете определить, является ли поток потоком демона, вызвав метод isDaemon(), или вы можете вызвать метод setDaemon(), чтобы установить поток в качестве потока демона.
группа потоков
Группа потоков — это концепция, уникальная для Java. В Java группа потоков — это объект класса ThreadGroup. Каждый поток принадлежит уникальной группе потоков. Эта группа потоков указывается при создании потока и не может использоваться в течение всего жизненного цикла потока. нить. Вы можете указать группу потоков, к которой принадлежит поток, вызвав конструктор класса Thread, содержащий параметр типа ThreadGroup. Если он не указан, поток по умолчанию использует системную группу потоков с именем system.
В Java все группы потоков должны создаваться явно, за исключением предварительно созданных системных групп потоков. В Java каждая группа потоков, за исключением группы системных потоков, принадлежит другой группе потоков. Вы можете указать группу потоков, к которой она принадлежит, при создании группы потоков. Если она не указана, по умолчанию она принадлежит к группе системных потоков. Таким образом, все группы потоков образуют дерево, корнем которого является системная группа потоков.
Java позволяет нам одновременно работать со всеми потоками в группе потоков. Например, мы можем установить приоритет всех потоков в ней, вызвав соответствующий метод группы потоков, а также запустить или заблокировать все потоки в ней. это.
Другая важная роль механизма групп потоков Java — безопасность потоков. Механизм групп потоков позволяет нам различать потоки с разными характеристиками безопасности посредством группировки, по-разному обрабатывать потоки в разных группах и поддерживать принятие неравных мер безопасности посредством иерархической структуры групп потоков. Класс Java ThreadGroup предоставляет большое количество методов, упрощающих работу с каждой группой потоков в дереве групп потоков и с каждым потоком в группе потоков.
Состояние потока В данный момент времени поток может находиться только в одном состоянии.
НОВЫЙ
В этом состоянии находятся еще не запущенные потоки.
РАБОТАЕМЫЙ
В этом состоянии находятся потоки, выполняющиеся на виртуальной машине Java.
ЗАБЛОКИРОВАНО
В этом состоянии находится поток, который заблокирован и ожидает блокировки монитора.
ОЖИДАЮЩИЙ
В этом состоянии находится поток, который бесконечно ожидает, пока другой поток выполнит определенную операцию.
Статус ожидающего потока. Поток находится в состоянии ожидания, поскольку он вызвал один из следующих методов:
Object.wait без значения тайм-аута
Thread.join без значения тайм-аута
LockSupport.park
Поток в состоянии ожидания ожидает, пока другой поток выполнит определенную операцию. Например, поток, вызвавший Object.wait() для объекта, ожидает, пока другой поток вызовет Object.notify() или Object.notifyAll() для этого объекта. Поток, вызвавший Thread.join(), ожидает завершения указанного потока.
TIMED_WAITING
В этом состоянии находится поток, ожидающий выполнения другим потоком операции, зависящей от указанного времени ожидания.
Состояние ожидающего потока с указанным временем ожидания. Поток находится в состоянии ожидания по времени, вызывая один из следующих методов с указанным положительным временем ожидания:
Thread.sleep
Object.wait со значением тайм-аута
Thread.join со значением тайм-аута
LockSupport.parkNanos
LockSupport.parkUntil
ПРЕКРАЩЕНО
В этом состоянии находится завершенный поток.