O que é condição
- A condição é uma interface
- Ele fornece um método análogo ao Objeto do monitor, com o Bloqueio pode ser implementado o modo de espera / notificação .
- Comparado ao método do monitor do objeto
- Pode suportar várias filas de espera
- Suporte a interrupção enquanto espera
- Tempo limite de espera de suporte
- A criação do objeto Condition depende do objeto Lock
- No código-fonte do Lock, podemos ver
Condition newCondition();
a definição de Get Condition
- No código-fonte do Lock, podemos ver
- A realização da condição é realizada em AQS
Aqui, precisamos dizer que um objeto de bloqueio corresponde a várias condições.
O significado aqui é equivalente ao de todos os threads sob o mesmo bloqueio (recurso), existem diferentes relacionamentos condicionais entre eles, então precisamos definir as condições separadamente
Uso de condição e análise de código-fonte
Conforme mencionado na introdução anterior, a Condição é definida em Lock.
Então, quando precisamos usar a Condição, precisamos lock对象.newContion()
obtê- la por meio de métodos.
Em seguida, você pode usar o método de condição object.await () para inserir a espera e liberar o bloqueio ao mesmo tempo.
Em seguida, outros objetos ativam os threads em espera chamando o método object.signal () da condição e liberam o bloqueio ao mesmo tempo.
Realização da Condição
A condição é uma interface e sua implementação específica está em AQS, que existe como uma classe interna de AQS.
A lógica aqui é mais clara, porque Condition depende da existência de Lock, e Lock deve usar AQS, então o arranjo é assim. Deve haver AQS no objeto Lock e deve haver uma Condição.
No código-fonte AQS, podemos encontrar a classe de implementação ConditionObject of Condition:
public class ConditionObject implements Condition, java.io.Serializable
A interface Condition não define uma fila, mas sua classe de implementação define uma fila de espera.
- Esta é uma fila unilateral
- Para mantê-lo, os nós firstWaiter e lastWaiter são usados
- O nó aqui é um nó que reutiliza a fila de sincronização no AQS (para que cada nó tenha uma referência de encadeamento)
- Cada objeto de condição tem uma fila de espera
- Um objeto de bloqueio pode ter vários objetos de condição, portanto, um objeto de bloqueio tem uma fila de sincronização e várias filas de espera de condição
Vamos dar uma olhada no processo de Condição de espera, fila de espera e notificação:
esperar:
- Em primeiro lugar, deve ficar claro que, como wait (), os threads que podem realizar espera e notificação são todos os threads que adquiriram o bloqueio (ou estado de sincronização), portanto, não há problema de sincronização aqui. O bloqueio nos ajuda resolver o problema de segurança do thread.
- Quando um thread executa o método await (), ele libera o bloqueio e, em seguida, constrói um novo nó para se juntar ao final da fila de espera e entrar no estado de espera.
- Em seguida, ele permanece na fila e permanece lá até que alguém sinalize, acorde a fila de espera e adiciona o primeiroWaiter (nó) da fila de espera ao final da fila de sincronização. Até eu entrar na fila de sincronização
Aviso prévio:
- Como await (), a thread executando o método de sinal também adquire o bloqueio
- Ele libera o bloqueio e, em seguida, espera que o firstWaiter da fila se mova para a fila síncrona e, em seguida, ativa o nó.
Vamos dar uma olhada no código-fonte: o
primeiro é await ():
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//当前节点加入等待队列
Node node = addConditionWaiter();
//释放同步状态
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
Veja meus comentários no código
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
Aqui é criar um nó, o modo do nó é Condição e, em seguida, inseri-lo no final da fila de espera
final int fullyRelease(Node node) {
try {
int savedState = getState();
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
Você pode ver que o estado de sincronização é liberado aqui e você realmente precisa chamar o método tryRelease do AQS para liberar o estado de sincronização.
Em seguida, chame o método LockSupport para bloquear o thread.
Em seguida, observe o método de sinal:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);//这里
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&//看这
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);//进入同步队列
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);//唤醒
return true;
}
Pode-se ver aqui que durante o sinal, o objeto desperto é o firstWaiter.
Após a chamada camada por camada, o nó é primeiro adicionado à fila de sincronização por meio da função enq e, em seguida,
o thread firstWaiter é ativado usando o método unpark de LockSupport.