Como herramienta para compartir datos simultáneamente y garantizar la coherencia, los bloqueos tienen múltiples implementaciones en la plataforma JAVA (como sincronizado y ReentrantLock, etc.). Estos candados ya escritos brindan comodidad para nuestro desarrollo, pero rara vez se mencionan la naturaleza y el tipo específicos de los candados. Esta serie de artículos analizará los nombres y características de bloqueos comunes en JAVA para responder sus preguntas.
1. Bloqueo de giro
Los bloqueos de giro se implementan permitiendo que el hilo actual se ejecute continuamente dentro del cuerpo del bucle. Solo cuando otros subprocesos cambian las condiciones del bucle se puede ingresar a la sección crítica. Copie el código de la siguiente manera:
clase pública SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();
bloqueo de vacío público(){
Hilo actual = Thread.currentThread();
while(!signo .compareAndSet(nulo, actual)){
}
}
desbloqueo público de vacío (){
Hilo actual = Thread.currentThread();
firmar .compareAndSet (actual, nulo);
}
}
Utilizando operaciones atómicas CAS, la función de bloqueo establece el propietario en el hilo actual y predice que el valor original está vacío. La función de desbloqueo establece el propietario en nulo y el valor previsto es el hilo actual.
Cuando un segundo subproceso llama a la operación de bloqueo, debido a que el valor del propietario no está vacío, el bucle se ejecuta hasta que el primer subproceso llama a la función de desbloqueo para establecer el propietario en nulo y el segundo subproceso puede ingresar a la sección crítica.
Dado que el bloqueo de giro solo mantiene el hilo actual ejecutando el cuerpo del bucle sin cambiar el estado del hilo, la velocidad de respuesta es más rápida. Pero cuando la cantidad de subprocesos continúa aumentando, el rendimiento disminuye significativamente porque cada subproceso debe ejecutarse y consume tiempo de CPU. Si la competencia de hilos no es intensa y el bloqueo se mantiene por un período de tiempo. Adecuado para usar con cerraduras giratorias.
Nota: Este ejemplo es un candado injusto. El orden de obtención del candado no se basará en el orden de entrada del candado.
2. Otros tipos de cerraduras giratorias
Hablamos anteriormente sobre los bloqueos giratorios. Hay tres formas de bloqueo comunes en los bloqueos giratorios: TicketLock, CLHlock y MCSlock.
El bloqueo de tickets resuelve principalmente el problema de la secuencia de acceso. El problema principal está en la CPU de múltiples núcleos:
Copie el código de código de la siguiente manera:
paquete com.alipay.titan.dcc.dal.entity;
importar java.util.concurrent.atomic.AtomicInteger;
clase pública TicketLock {
número de servicio AtomicInteger privado = nuevo AtomicInteger();
número de billete de AtomicInteger privado = nuevo AtomicInteger();
privado estático final ThreadLocal<Integer> LOCAL = new ThreadLocal<Integer>();
bloqueo de vacío público () {
int miticket = ticketNum.getAndIncrement();
LOCAL.set(mibillete);
while (mibillete!= númeroservicio.get()) {
}
}
desbloqueo público vacío() {
int miticket = LOCAL.get();
serviceNum.compareAndSet(miboleto, miboleto + 1);
}
}
Se debe consultar un número de servicio serviceNum cada vez, lo que afecta el rendimiento (debe leerse desde la memoria principal y se debe evitar que otras CPU lo modifiquen).
CLHLock y MCSLock son dos tipos similares de bloqueos justos, ordenados en forma de lista vinculada.
Copie el código de código de la siguiente manera:
importar java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
clase pública CLHLock {
clase estática pública CLHNode {
booleano volátil privado isLocked = verdadero;
}
@SuppressWarnings("no utilizado")
cola de CLHNode volátil privada;
hilo final estático privado<CLHNode> LOCAL = nuevo ThreadLocal<CLHNode>();
final estático privado AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock.class,
CLHNode.class, "cola");
bloqueo de vacío público () {
Nodo CLHNode = nuevo CLHNode();
LOCAL.set(nodo);
CLHNode preNodo = UPDATER.getAndSet(este, nodo);
if (preNodo! = nulo) {
mientras (preNode.isLocked) {
}
preNodo = nulo;
LOCAL.set(nodo);
}
}
desbloqueo público vacío() {
Nodo CLHNode = LOCAL.get();
if (!UPDATER.compareAndSet(este, nodo, nulo)) {
nodo.isLocked = falso;
}
nodo = nulo;
}
}
CLHlock consulta continuamente variables precursoras, lo que lo hace inadecuado para su uso en la arquitectura NUMA (en esta arquitectura, cada subproceso se distribuye en un área de memoria física diferente)
MCSLock recorre los nodos de las variables locales. No hay ningún problema con CLHlock.
Copie el código de código de la siguiente manera:
importar java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
clase pública MCSLock {
clase estática pública MCSNode {
MCSNode volátil a continuación;
booleano volátil isLocked = verdadero;
}
hilo final estático privado<MCSNode> NODO = nuevo ThreadLocal<MCSNode>();
@SuppressWarnings("no utilizado")
cola MCSNode volátil privada;
final estático privado AtomicReferenceFieldUpdater<MCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,
MCSNode.class, "cola");
bloqueo de vacío público () {
MCSNode currentNode = nuevo MCSNode();
NODO.set (nodo actual);
MCSNode preNode = UPDATER.getAndSet(este, currentNode);
if (preNodo! = nulo) {
preNodo.next = nodoactual;
mientras (currentNode.isLocked) {
}
}
}
desbloqueo público vacío() {
MCSNode currentNode = NODO.get();
si (currentNode.next == nulo) {
if (UPDATER.compareAndSet(este, nodoactual, nulo)) {
} demás {
mientras (currentNode.next == nulo) {
}
}
} demás {
currentNode.next.isLocked = falso;
currentNode.next = nulo;
}
}
}
Desde el punto de vista del código, CLH es más sencillo que MCS.
La cola CLH es una cola implícita y no tiene atributos de nodo sucesor real.
La cola MCS es una cola explícita con atributos de nodo sucesor real.
El bloqueo predeterminado utilizado internamente por JUC ReentrantLock es el bloqueo CLH (hay muchas mejoras, como reemplazar los bloqueos giratorios con bloqueos de bloqueo, etc.).
(Finaliza el texto completo)