Este artigo é para ler notas e livros sobre a arte da programação simultânea em Java
1. Bloqueio justo
Justiça é para adquirir bloqueios Se um bloqueio for justo, a ordem de aquisição de bloqueios deve estar em conformidade com a ordem de tempo absoluto da solicitação, ou seja, FIFO.
Ao usar o bloqueio justo, o rastreamento de chamada do método de bloqueio lock () é o seguinte.
1) ReentrantLock: lock ().
2) FairSync: lock ().
3) AbstractQueuedSynchronizer: adquirir (int arg).
4) ReentrantLock: tryAcquire (int adquire).
Na etapa 4, o bloqueio é realmente iniciado.A seguir está o código fonte do método
//java/util/concurrent/locks/AbstractQueuedSynchronizer.java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//java.util.concurrent.locks.ReentrantLock.FairSync
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// private volatile int state; 在 AbstractQueuedSynchronizer
int c = getState();//获取锁的开始,首先读volatile变量state
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;
}
解锁:
1) ReentrantLock: unlock ()。
2) AbstractQueuedSynchronizer: release (int arg)。
3) Sync: tryRelease (int releases)。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
Bloqueio injusto
Desde que o estado de sincronização seja definido com sucesso durante a rotação, isso significa que o bloqueio é adquirido
.
1) ReentrantLock: lock ().
2) NonfairSync: lock ().
3) AbstractQueuedSynchronizer: compareAndSetState (int expect, int update).
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//获取不到锁
acquire(1);
}
//AbstractQueuedSynchronizer:compareAndSetState
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
Desbloqueie o mesmo bloqueio justo
3. O status do bloqueio não pode ser obtido
static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
m1();
}, "t1").start();
Thread.sleep(10);
//观察t2
new Thread(() -> {
m2();
}, "t2").start();
}
public static void m1() {
reentrantLock.lock();
try {
try {
//睡眠
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
reentrantLock.unlock();
}
}
public static void m2() {
System.out.println("尝试获取锁");
reentrantLock.lock();
try {
System.out.println("获取到锁");
} finally {
reentrantLock.unlock();
}
}
Sabemos que os bloqueios sincronizados não podem ser adquiridos e o estado desses dois bloqueios é diferente.Por
esse motivo, depende do código fonte
public final void acquire(int arg) {
// 再次尝试获取状态,如果还是获取不到就把 当前线程加入队列
//返回值是线程是否在自旋的过程中被挂起(park),如果被挂起了,就唤醒
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();// Thread.currentThread().interrupt();
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//死循环
for (;;) {
// 得到前一个 节点
final Node p = node.predecessor();
//以下为自旋过程
// 查看是不是头结点 如果是 就尝试获取锁 获取成功
if (p == head && tryAcquire(arg)) {
//把当前节点设置为头结点
setHead(node);
p.next = null; // help GC
//是否失败获取 表明成功获取状态
failed = false;
//退出自旋,放回线程是否被挂起 (park)
return interrupted;
}
//以上的条件都没有满足,就把当前线程park
// 检查和更新未能获取的节点的状态。
//如果线程阻塞,返回true。这是主信号
//控制所有获取循环。需要pred == node.prev。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//结束的时候,如果还是获取不到状态,那就取消这个节点了
if (failed)
//取消正在进行的获取尝试。
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
// 挂起挂起
LockSupport.park(this);
//返回当前线程状态 true
return Thread.interrupted();
}
Portanto, o encadeamento está no estado de espera, ou seja, o encadeamento é bloqueado durante o processo de rotação;
ou seja, se (shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ()) esse código-chave
4. Resumo
❑ Quando um bloqueio justo e um bloqueio injusto são liberados, um estado variável volátil deve ser gravado no final.
❑ Ao adquirir um bloqueio justo, ele primeiro lê a variável volátil.
❑ Ao adquirir um bloqueio injusto, a variável volátil é atualizada primeiro com o CAS.Esta operação possui a semântica da memória de leitura e gravação voláteis.
Existem pelo menos duas maneiras de implementar a semântica de memória da aquisição de liberação de bloqueio:
1) Use a semântica de memória de leitura e gravação usando variáveis voláteis.
2) Use a semântica de memória de leitura volátil e gravações voláteis anexadas ao CAS.