опыт
О реентерабельных блокировках я слышал давно.Что значит реентерабельные блокировки?Я их проглатывал целиком.Только помните,что 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 для подсчета количества повторных входов, что позволяет избежать частых операций удержания и освобождения, что не только повышает эффективность, но и предотвращает взаимоблокировки.