Глубокое понимание ReentrantLock

Java задняя часть
Глубокое понимание ReentrantLock

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

Нажмите, чтобы узнать подробностиwww.codercc.com

1. Введение в ReentrantLock

ReentrantLock — это класс, реализующий интерфейс Lock, а также блокировка, часто используемая в реальном программировании.Поддерживает повторный вход, что означает, что общий ресурс может быть заблокирован повторно, то есть текущий поток снова получает блокировку, не будучи заблокированным.. Синхронизированное ключевое слово в java неявно поддерживает повторный вход (при синхронизированном вы можетесм. эту статью), синхронизированный достигает повторного входа, приобретая автоинкремент и отключая автодекремент. В то же время ReentrantLock также поддерживаетЧестные и нечестные замкиДва пути. Затем, если вы хотите полностью понять ReentrantLock, главное изучить семантику синхронизации ReentrantLock: 1. Принцип реализации реентерабельности 2. Честная блокировка и нечестная блокировка.

2. Реализация принципа реентерабельности

Для поддержки повторного входа необходимо решить две проблемы: ** 1. Когда поток получает блокировку, если поток, который получил блокировку, является текущим потоком, он будет получен снова напрямую; 2. Поскольку блокировка будет получена n раз , то только после того, как блокировка будет снята такое же n раз, блокировка может считаться полностью успешно снятой. **пройти черезэта статья, мы знаем, что компоненты синхронизации выражают свою семантику синхронизации, в основном, переписывая несколько защищенных методов AQS. Что касается первого вопроса, давайте посмотрим, как реализован ReentrantLock.В качестве примера возьмем несправедливую блокировку, чтобы определить, может ли текущий поток получить блокировку в качестве примера.Основным методом является nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //1. 如果该锁未被任何线程占有,该锁能被当前线程获取
	if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
	//2.若被占有,检查占有线程是否是当前线程
    else if (current == getExclusiveOwnerThread()) {
		// 3. 再次获取,计数加一
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

Логика этого кода также очень проста, подробности смотрите в комментариях. Для поддержки повторного входа на втором этапе добавляется логика обработки.Если блокировка уже занята потоком, он продолжит проверку того, является ли занятый поток текущим потоком.Если это так, состояние синхронизации будет увеличено на 1, чтобы вернуть true, указывая на то, что его можно получить снова. Каждый раз, когда вы повторно получаете, состояние синхронизации будет увеличиваться на единицу, так в чем заключается идея обработки, когда вы его выпускаете? (Все еще беря в качестве примера несправедливую блокировку) Основным методом является tryRelease:

protected final boolean tryRelease(int releases) {
	//1. 同步状态减1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
		//2. 只有当同步状态为0时,锁成功被释放,返回true
        free = true;
        setExclusiveOwnerThread(null);
    }
	// 3. 锁未被完全释放,返回false
    setState(c);
    return free;
}

См. комментарии в логике кода. Следует отметить, что для успешного снятия блокировки с повторным входом необходимо дождаться состояния синхронизации 0:00, иначе блокировка еще не снята. Если блокировка получена n раз, освобождение n-1 раз, блокировка не полностью снята, возвращает false, только для освобождения n раз для успешного освобождения возвращается true. Теперь мы можем разобраться, как добиться реентерабельности ReentrantLock, то есть понять семантику первой синхронизации.

3. Справедливая блокировка и нечестная блокировка

ReentrantLock поддерживает два типа блокировок:честный замокинесправедливый замок.Что такое справедливость для приобретения блокировок.Если блокировка является справедливой, то порядок приобретения блокировок должен соответствовать абсолютному порядку времени по запросу и удовлетворять FIFO. ReentrantLock的构造方法无参时是构造非公平锁,源码为:

public ReentrantLock() {
    sync = new NonfairSync();
}

Кроме того, предоставляется еще один метод. Вы можете передать логическое значение. Если значение равно true, это справедливая блокировка, а если false — нечестная блокировка. Исходный код:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

В описанном выше получении недобросовестной блокировки (метод nonfairTryAcquire) он просто получает текущее состояние и выполняет некоторую логическую обработку без учета состояния ожидания потоков в текущей очереди синхронизации. Давайте посмотрим на логику обработки честных блокировок.Основной метод:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
  }
}

Логика этого кода в основном такая же, как у nonfairTryAcquire. Единственное отличие состоит в том, что добавлено логическое суждение hasQueuedPredecessors. Имя метода можно использовать для определения того, есть ли у текущего узла предшествующий узел в очереди синхронизации. Если есть предшествующий узел, описание Есть потоки, которые запрашивают ресурсы раньше, чем текущий поток Согласно справедливости, текущий поток не может запросить ресурсы. Если у текущего узла нет узла-предшественника, необходимо сделать последующие логические выводы.Справедливая блокировка каждый раз получает блокировку с первого узла в очереди синхронизации, в то время как нечестная блокировка не обязательно такова, поскольку поток, только что снявший блокировку, может снова получить блокировку..

Справедливая блокировка против несправедливой блокировки

  1. Каждый раз, когда справедливая блокировка получает блокировку, она становится первым узлом в очереди синхронизации.Гарантировать абсолютный порядок запрашиваемых ресурсов во времени, несправедливая блокировка может привести к тому, что поток, который только что освободил блокировку, продолжит получать блокировку в следующий раз, что может привести к тому, что другие потоки никогда не получат блокировку.вызывают «голодание».

  2. Чтобы обеспечить абсолютный порядок во времени, справедливые блокировки требуют частых переключений контекста, в то время как нечестные блокировки уменьшат определенные переключения контекста и накладные расходы на производительность. Поэтому ReentrantLock по умолчанию выбирает несправедливые блокировки, чтобы уменьшить некоторые переключения контекста,Гарантированная большая пропускная способность системы.

использованная литература

Искусство параллельного программирования на Java