Что такое реентерабельная блокировка?

задняя часть
Что такое реентерабельная блокировка?

опыт

О реентерабельных блокировках я слышал давно.Что значит реентерабельные блокировки?Я их проглатывал целиком.Только помните,что ReentrantLock и synchronized-это реентерабельные блокировки.Вы можете использовать их как хотите,ладно,простите меня,я был невежествен.Недавно , я проверил основы и заполнил пробелы. Я обнаружил, что ничего об этом не знаю. Я быстро просмотрел это и почувствовал, что необходимо написать блог, чтобы объяснить это. Просто притворись, что ничего не произошло, хе-хе. . .

Парафраз

Реентерабельная блокировка в широком смысле относится к блокировке, которую можно многократно и рекурсивно вызывать.После того, как блокировка используется во внешнем слое, она все еще может использоваться во внутреннем слое, и взаимоблокировка не возникает (при условии, что это тот же объект или класса) Такая блокировка называется реентерабельной блокировкой. И ReentrantLock, и synchronized являются повторными блокировками.Ниже приведен пример использования synchronized:

public class ReentrantTest implements Runnable {

    public synchronized void get() {
        System.out.println(Thread.currentThread().getName());
        set();
    }

    public synchronized void set() {
        System.out.println(Thread.currentThread().getName());
    }

    public void run() {
        get();
    }

    public static void main(String[] args) {
        ReentrantTest rt = new ReentrantTest();
        for(;;){
            new Thread(rt).start();
        }
    }
}

Во всем процессе нет взаимоблокировки, и некоторые выходные результаты выглядят следующим образом:

Thread-8492
Thread-8492
Thread-8494
Thread-8494
Thread-8495
Thread-8495
Thread-8493
Thread-8493

И set(), и get() выводят имя потока, указывая на то, что взаимоблокировка не возникает, даже если синхронизация используется рекурсивно, доказывая, что она является реентерабельной.

блокировка без повторного входа

Блокировки без повторного входа, в отличие от блокировок с повторным входом, не могут быть вызваны рекурсивно, и при рекурсивных вызовах возникнут взаимоблокировки. См. классическое объяснение, используя спин-блокировку для имитации блокировки без повторного входа, код выглядит следующим образом:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();

    public void lock() {
        Thread current = Thread.currentThread();
        //这句是很经典的“自旋”语法,AtomicInteger中也有
        for (;;) {
            if (!owner.compareAndSet(null, current)) {
                return;
            }
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

Код также относительно прост. Атомарные ссылки используются для хранения потоков. Один и тот же поток дважды вызывает метод lock(). Если unlock() не выполняется для снятия блокировки, при вызове второго цикла возникает взаимоблокировка. lock не является реентерабельным, но на самом деле один и тот же поток не должен снимать блокировку, а затем получать блокировку каждый раз Такое переключение планирования очень ресурсоемко. Немного измените его, чтобы сделать блокировку с повторным входом:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();
    private int state = 0;

    public void lock() {
        Thread current = Thread.currentThread();
        if (current == owner.get()) {
            state++;
            return;
        }
        //这句是很经典的“自旋”式语法,AtomicInteger中也有
        for (;;) {
            if (!owner.compareAndSet(null, current)) {
                return;
            }
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        if (current == owner.get()) {
            if (state != 0) {
                state--;
            } else {
                owner.compareAndSet(current, null);
            }
        }
    }
}

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

Реализация реентерабельной блокировки в ReentrantLock

Вот посмотрите на метод получения блокировки для несправедливых блокировок:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //就是这里
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

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