«Сквоттинг также может попасть на большую фабрику», многопоточный анализ исходного кода серии Lock.

Java задняя часть

Это мой 16-й день в Gengwen Challenge, ознакомьтесь с подробностями мероприятия:Обновить вызов

Автор: Флауэр Джи

Публичный аккаунт WeChat: разработка Java ноль к одному

предисловие

Многопоточная серия Мы обновили несколько глав впереди, настоятельно рекомендуем маленьким партнерам изучать по порядку:

«В гигантскую яму можно» многопоточная серия статей-каталогов

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

текст

Определение блокировки

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();
    
    Condition newCondition();
}

как получить замок

Интерфейс блокировки определяет четыре способа получения блокировки:

  • lock()
    • блокировка выборки, когда блокировка не получена, текущий поток будет бездействовать и не будет участвовать в планировании потока, пока блокировка не будет получена,Процесс получения блокировки не отвечает на прерывания.
  • lockInterruptibly()
    • блокировка выборки,а такжепрерываемый, метод вызовет InterruptedException, если произойдет одно из двух событий.
      • При вызове этого метода флаг прерывания потока был установлен в значение true.
      • В процессе получения блокировки поток прерывается, и реализация получения блокировки будет реагировать на это прерывание.
    • После создания InterruptedException флаг прерывания текущего потока будет очищен.
  • tryLock()
    • неблокирующая выборка, как видно из названия, try означает попробовать, неважно, успешно это или нет, метод возвращает сразу
    • По сравнению с двумя предыдущими методами получения блокировки, этот метод имеет возвращаемое значение.Если получение блокировки прошло успешно, он возвращает true, а если получение блокировки не удалось, он возвращает false.
  • tryLock(long time, TimeUnit unit)
    • С механизмом тайм-аута и возможностью прерывания
    • Немедленно возвращает true, если блокировка полосы может быть получена
    • Если блокировка не может быть получена, текущий поток будет бездействовать и не будет участвовать в планировании потока, пока не будет выполнено одно из следующих трех условий:
      • Текущий поток получил блокировку
      • Другой поток прервал текущий поток
      • Установленный тайм-аут истек
    • Метод вызовет InterruptedException, если произойдет одно из двух событий.
      • При вызове этого метода флаг прерывания потока был установлен в значение true.
      • В процессе получения блокировки поток прерывается, и реализация получения блокировки будет реагировать на это прерывание.
    • После создания InterruptedException флаг прерывания текущего потока будет очищен.
    • Если тайм-аут истекает, а текущий поток не получил блокировку, он вернет false напрямую (обратите внимание, что здесь не выдается исключение тайм-аута).

фактически,tryLock(long time, TimeUnit unit)Это больше похоже на комбинацию блокировки и неблокировки, то есть блокировку при определенных условиях (в течение тайм-аута прерывания не происходит) и немедленный возврат (неблокировку), если это условие не выполняется.

Четыре метода получения блокировки суммируются следующим образом:锁的获取

разблокировка замка

По сравнению с приобретением замков способ снятия замков намного проще, есть только один

void unlock();

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

Интерфейс Lock также определяет метод newCondition:

Condition newCondition();

Этот метод создаст объект Condition, привязанный к текущему объекту Lock, что означает, что объект Condition и объект Lock соответствуют друг другу.Объект Lock может создать несколько объектов Condition, которые находятся в отношениях «один ко многим».

Интерфейс условий

Как мы сказали выше, интерфейс Lock определяетnewConditionметод, который возвращает объект Condition, связанный с текущим объектом Lock Давайте посмотрим, что представляет собой этот объект Condition.

Каждый новый инструмент призван решить определенную проблему, и появление интерфейса Condition не является исключением. Если внешний вид интерфейса Lock должен расширить существующую блокировку монитора, то внешний вид интерфейса Condition должен расширить механизм ожидания и уведомления в блоке кода синхронизации.

Недостатки механизма ожидания/уведомления блокировки монитора

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

С другой стороны, из предыдущих статей, знакомящих с принципом синхронизации, мы знаем, что все потоки, вызывающие метод ожидания, будут заблокированы на одном мониторе.wait setКажется разумным ждать посередине, но это недостаток этого механизма — все потоки ожидают одного и того же метода уведомления.notify()а такжеnotifyAll()两个方法,下同)。 Each thread calling the wait method may wait on a different conditional predicate, but sometimes even if the condition it is waiting for is not met, the thread may be woken up by the "other thread's" notify method, because everyone uses the same monitor device замок.这就好比一个班上有几个重名的同学(使用相同的监视器锁),老师喊了这个名字(notify方法),结果这几个同学全都站起来了(等待在监视器锁上的线程都被唤醒了)。

Таким образом, даже после того, как вы проснулись, захватите блокировку монитора, обнаружите, что условие все еще не выполняется, или вам придется вызвать метод WAIT для зависания, что приведет к множеству интересных моментов и трате ресурсов ЦП.

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

Следовательно, лучший способ — указать, какой условный предикат приостанавливать при приостановке, и в то же время, после того, как произойдет событие ожидания, только разбудить поток, ожидающий этого события, и реализовать эту идею в интерфейсе Condition.

С помощью интерфейса Condition мы можем создавать разные условия пробуждения для одной и той же блокировки, чтобы после выполнения определенного условного предиката определенные потоки могли быть разбужены целевым образом, а не все ожидающие потоки.

Механизм ожидания/сигнала условия

Поскольку ранее было упомянуто, что появление интерфейса Condition должно расширить существующий механизм ожидания/уведомления, давайте взглянем на методы существующего механизма ожидания/уведомления:

public class Object {
    public final void wait() throws InterruptedException {
        wait(0);
    }
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException {
        // 这里省略方法的实现
    }
    public final native void notify();
    public final native void notifyAll();
}

Далее рассмотрим методы интерфейса Condition:

public interface Condition {
    void await() throws InterruptedException;
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    void awaitUninterruptibly();
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    void signal();
    void signalAll();
}

Сравнение, соответствующие отношения очевидны здесь:

Методы объекта Метод условия разница
void wait() void await()
void wait(long timeout) long awaitNanos(long nanosTimeout) единица времени, возвращаемое значение
void wait(long timeout, int nanos) boolean await(long time, TimeUnit unit) единица времени, тип параметра, возвращаемое значение
void notify() void signal()
void notifyAll() void signalAll()
- void awaitUninterruptibly() Эксклюзивное состояние
- boolean awaitUntil(Date deadline) Эксклюзивное состояние

Они похожи по спецификации интерфейса, но механизм ожидания/уведомления нацелен на все блокировки в мониторе.wait setПотоки в , и механизм ожидания/сигнала нацелены на все потоки, ожидающие условия.

Еще одно, что нужно сказать здесь, в спецификации интерфейса,wait(long timeout)Единицей времени являются миллисекунды, аawaitNanos(long nanosTimeout)Единицей времени являются наносекунды (nanoseconds), в связи с этим имя метода awaitNanos на самом деле более семантически понятно, и по сравнению сwait(long timeout, int nanos)Этот немного неуклюжий метод,await(long time, TimeUnit unit)Этот метод более интуитивен и эффективен.

Еще один момент, который стоит отметить, заключается в том, чтоawaitNanos(long nanosTimeout)дас возвращаемым значением, который возвращает оставшееся время ожидания;await(long time, TimeUnit unit)Существует также возвращаемое значение.Если метод возвращается из-за истечения времени ожидания, метод возвращает false, в противном случае он возвращает true.

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

Мы знаем, что когда поток возвращается из метода ожидания/ожидания с тайм-аутом, должно произойти одно из следующих четырех событий:

  1. Метод уведомления/сигнала вызывается другими потоками, и текущий поток оказывается тем, который был выбран для пробуждения.
  2. Другие потоки, называемые методами notifyAll/signalAll
  3. Другой поток прервал текущий поток
  4. Время ожидания истекло

Среди них третья выдаст InterruptedException, которую относительно легко отличить; удалите это,Когда метод ожидания возвращает значение, мы не можем на самом деле различить, возвращается ли он из-за того, что время ожидания истекло, или он возвращается уведомлением..但是对于await方法,因为它是有返回值的,我们就能够通过返回值来区分:

  • еслиawaitNanos(long nanosTimeout)Возвращаемое значение больше 0, что указывает на то, что время ожидания не истекло, а возврат вызван поведением сигнала.
  • еслиawait(long time, TimeUnit unit)Возвращает true, указывая, что время ожидания не истекло, тогда возврат вызван поведением сигнала

В комментариях к исходному коду также говорится:await(long time, TimeUnit unit)эквивалентно вызовуawaitNanos(unit.toNanos(time)) > 0

так,Их возвращаемые значения могут помочь нам понять, почему метод вернул.

В интерфейсе Condition также есть два метода, которых нет в Object:

void awaitUninterruptibly();
boolean awaitUntil(Date deadline) throws InterruptedException;

Все упомянутые выше методы ожидания/ожидания выбрасывают InterruptedException в своих сигнатурах методов, указывая на то, что все они реагируют на прерывания во время процесса ожидания.Не отвечает на прерывания в ожидании блокировки, поэтому InterruptedException не генерируется. То есть он блокируется до тех пор, пока не будет вызван signal/signalAll. Если поток прерывается во время этого процесса, он не отвечает на прерывание, но когда метод возвращается, флаг прерывания потока будет истинным, и вызывающая сторона может обнаружить этот флаг прерывания, чтобы помочь в оценке во время процесса ожидания. произошло прерывание, чтобы решить, следует ли выполнять дополнительную обработку.

boolean awaitUntil(Date deadline)а такжеboolean await(long time, TimeUnit unit)На самом деле эффект аналогичен, и значение возвращаемого значения такое же, за исключением того, что одно является относительным временем, а другое - абсолютным. Параметром метода awaitUntil является Date, который представляет абсолютное время, которое Это крайний срок. До этой даты метод будет ждать сигнала или прерывания.

Суммировать

На этом мы закончили анализ интерфейса Lock и интерфейса Condition. Далее будут проанализированы источники исходного кода, такие как ReentrantLock, CountDownLatch и Semaphore.

Будьте внимательны, чтобы не потерять

Выше приведено все содержание этого вопроса,Если есть какие-либо ошибки, пожалуйста, оставьте сообщение для совета, большое спасибо. Я Giegie. Если у вас есть какие-либо вопросы, не стесняйтесь оставлять сообщение для обсуждения. Увидимся в следующем номере.

Статья постоянно обновляется, вы можете искать на WeChatРазработка на Java ноль к одномуПрочтите это в первый раз и получите егоУчебное видео по материалам интервьюПодождите, заинтересованные друзья могут обратить внимание, учиться вместе и работать вместе 🐮🥃.

Оригинальность непроста, как можно терпеть проститутку даром?, если вы считаете, что эта статья вам полезна, спасибо старому железяку за эту статью点个赞、评论或转发一下, потому что это будет моей мотивацией выводить больше качественных статей, спасибо!