Directorio de artículos
Visión general
AQS es el marco básico para realizar varios bloqueos comerciales. Por ejemplo, la capa inferior de ReentrantLock es utilizar AQS. Podemos referirnos a ReentrantLock para implementar la lógica de bloqueo de nuestras propias necesidades específicas.
El marco AQS en sí mismo se basa en dos herramientas poderosas, Unsafe y LockSupport, que se puede decir que son la mano derecha.
Por lo tanto, comprender cómo se utilizan Unsafe y LockSupport en AQS nos ayuda a comprender los principios de implementación subyacentes de varios bloqueos y también puede responder sistemáticamente a preguntas relacionadas con AQS durante las entrevistas.
AQS 与 Inseguro
El uso de Inseguro en AQS es principalmente operación CAS.
P.ej:
/**
* CAS head field. Used only by enq.
* 入队时 CAS 修改队列 head
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
* 入队时 CAS 修改队列 tail
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
AQS 与 LockSupport
El uso de LockSupport en AQS se usa principalmente para el parque de subprocesos después de la puesta en cola y un subproceso en la cola de anulación de estacionamiento.
// 未获取到锁的线程被包装成 Node,然后加入队列中
// 进入队列的 Node 如果处在 head,则尝试抢锁一次
// 若不在 head 位置或在 head 抢锁不成功,则进入 park
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)) {
// 前一个节点是head,则尝试抢锁一次
setHead(node); // 抢锁成功,自己变为 head
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 若 Lock 的逻辑是抢锁失败后 park,则这里让线程进入 park
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 入队的线程 park
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
LockSupport 与 Inseguro
Los dos métodos principales de LockSupport, park () y unpark (Thread thread), se implementan internamente a través de métodos Unsafe.
// LockSupport 的 park 方法直接调用 Unsafe#park
public static void park() {
UNSAFE.park(false, 0L);
}
// LockSupport 的 unpark 方法直接调用 Unsafe#unpark
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
Principio de desaparición de parque inseguro
En el sistema Linux, se implementa utilizando el mutex (exclusión mutua) y la condición (variable de condición) en la biblioteca de subprocesos Posix pthread. Y en el proceso de park unpark, se protege una variable _counter.
Al llamar a park, primero intente obtener el "permiso" directamente, es decir, si se juzga _counter> 0, si tiene éxito, configure _counter en 0 y regrese. De lo contrario, ingrese a la espera.
Al llamar a unpark, establezca directamente _counter en 1 y luego desbloquee el mutex para regresar. Si el valor anterior de _counter es 0, se debe llamar a pthread_cond_signal para despertar los hilos que esperan en el parque.
La explicación del código fuente puede referirse al análisis del código fuente de LockSupport (aparcar / desempacar)
La diferencia entre LockSupport y esperar notificación
- esperar notificar debe ser llamado después de sincronizado adquiere el bloqueo
- notificar notificar Todo no puede despertar un hilo con precisión, unpark (hilo) puede hacerlo
- LockSupport # unpark (Thread) se puede ejecutar antes que Thread park (), en este caso, Thread park () regresará inmediatamente (porque el _counter subyacente> 0)
【referencia】
- https://www.jianshu.com/p/e3afe8ab8364