Состояние потоков, блокировок и условий
В Java, если поток, t2
пытается получить синхронизированную блокировку, которая в данный момент используется другим потоком, t1
, затем t2
переключится с работоспособного на заблокированный. Правильный? Как насчет с ReentrantLock
s?
Если нить t1
заканчивает с помощью блокировки, делает t2
затем автоматически переключиться обратно на работоспособный или вам нужно использовать notifyAll()
? Как насчет с ReentrantLock
использование без условия. Если вы не используете условие, как вы сообщите нить t2
что он должен переключиться обратно на работоспособный? Разумно ли или даже возможно использовать входящие замки без каких-либо условий?
Если на этот вопрос уже был дан ответ (я не смог его найти), я был бы признателен, если бы вы связали его со мной.
2 ответа
Похоже, вы путаете заблокированные и ожидающие состояния. Заблокированный означает, что поток пытается получить блокировку и не может так застрять. Ожидание означает, что поток неактивен; он зависает до тех пор, пока не получит уведомление, или пока он не вернется из режима ожидания (тайм-аут, если вызывается со значением тайм-аута, или ложное пробуждение).
Как только блокировка становится доступной, планировщик ОС должен решить, какой заблокированный поток ее получит. Поток, который он выбирает для получения блокировки, становится работоспособным.
Таким образом, уведомление относится к ожидающим потокам, а не заблокированным. Поток, который имеет блокировку, но обнаружил, что не может прогрессировать (он обнаруживает, что условие, которого он ожидает, не соответствует действительности), может вызвать ожидание для этой блокировки, сняв блокировку и перейдя в состояние ожидания. Вы используете notify, чтобы сообщить планировщику, чтобы он пробудил любой поток, ожидающий блокировки. Как только поток проснулся, он должен снова получить блокировку, которую он ранее снял, прежде чем он сможет выйти из метода ожидания.
Основное поведение ReentrantLock аналогично встроенным блокировкам, за исключением того, что вы можете иметь несколько условий с повторяющимися блокировками. Имейте в виду, что ReentrantLock имеет свои собственные отдельные методы для вызова (ожидание и сигнал вместо ожидания и уведомления). Вы должны использовать условия с ReentrantLock, когда хотите, чтобы потоки ожидали и получали уведомления, с другими используемыми условиями, так что потоки будут ожидать только при соответствующих им условиях.
Если поток t2 пытается синхронизироваться с блокировкой, которая в данный момент используется другим потоком t1 - например, путем попытки войти в синхронизированный блок, когда t1 уже находится в синхронизированном блоке с той же блокировкой - тогда t2 заблокирует, да. Это также верно для реентерабельных замков, включая ReentrantLock
учебный класс; Следует отметить, что блокировки по умолчанию в Java повторно используются (подробнее об этом позже).
Если t1 снимает блокировку по умолчанию, например, выходя из синхронизированного блока, то t2 разблокируется; это особенность языка. Однако, если вы используете ReentrantLock, поток, содержащий блокировку, должен явно вызвать ReentrantLock.unlock()
снять блокировку, как это должно было быть ReentrantLock.lock()
получить замок.
Обратите внимание, что "повторный вход" относится к тому, может ли отдельный поток "повторно входить" в синхронизированные блоки, а не к любому взаимодействию между потоками. Повторно входящие блокировки могут быть снова заблокированы потоками, которые уже удерживают блокировку; не возвращающиеся замки не могут. Обратите внимание, что в Java, если один поток получает повторно входящую блокировку более одного раза, он должен снять блокировку столько же раз, прежде чем другие потоки, ожидающие блокировку, будут разблокированы. Для блокировок по умолчанию это происходит естественным образом с вложенными синхронизированными блоками, возможно, на разных уровнях вызова функций.