La tercera parte del subproceso múltiple JAVA (1) Bloqueo explícito y sincronizado

Portal de notas concurrentes:
1.0 Programación concurrente-Mapa mental
2.0 Programación concurrente-Fundamentos de seguridad de subprocesos
3.0 Programación concurrente-Módulo de construcción básico
4.0 Programación concurrente-Ejecución de tareas-Futuro
5.0 Programación concurrente-Rendimiento y escalabilidad de subprocesos múltiples
6.0 Programación concurrente- Bloqueo explícito y
programación concurrente sincronizada 7.0-AbstractQueuedSynchronizer
8.0 programación concurrente-variables atómicas y mecanismo de sincronización sin bloqueo

Bloqueo explícito

Antes de Java 5, el único mecanismo que se puede utilizar al coordinar el acceso a los objetos compartidos es la synchronizedsuma volatile. Se ha agregado Java 5 ReentrantLock. ReentrantLockNo es una forma de reemplazar el bloqueo integrado, sino una función avanzada opcional cuando el mecanismo de bloqueo integrado no es aplicable.

Bloquear 与 ReentrantLock

Lock proporciona una operación de adquisición incondicional, sondeada, temporizada e interrumpible Todos los métodos de bloqueo y desbloqueo son explícitos.

En la implementación de Lock, se debe proporcionar la misma semántica de visibilidad de memoria que los bloqueos internos, pero pueden ser diferentes en términos de semántica de bloqueo, algoritmos de programación, garantías de orden y características de rendimiento.

package java.util.concurrent.locks;
/**
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {
    
    

    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

ReentrantLockImplementa Lockla interfaz y proporciona la synchronizedmemoria visible y la misma fila del mutex. Y synchronizedcomo lo mismo, ReentrantLocktambién proporciona 可重入(可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。)semántica de bloqueo.

¿Por qué crear un nuevo mecanismo de bloqueo que sea tan similar a los bloqueos de memoria?

En la mayoría de los casos, la cerradura integrada funciona bien, pero existen algunas limitaciones en la funcionalidad. Por ejemplo, no puede interrumpir un hilo que está esperando para adquirir un bloqueo, o no puede esperar indefinidamente mientras solicita un bloqueo.
El candado incorporado debe liberarse en el bloque de código que adquirió el candado, lo que simplifica el trabajo de codificación y logra una buena interacción con las operaciones de manejo de excepciones, pero no puede implementar las reglas de bloqueo de la estructura sin bloqueo. Estas son synchronizedlas razones para su uso , pero en algunos casos, un mecanismo de bloqueo más flexible generalmente puede proporcionar una mejor vivacidad o rendimiento.

Para llamar a Lock explícitamente, el bloqueo debe liberarse finalmente. Aunque no es difícil liberar el bloqueo finalmente, puede olvidarse.

Bloqueo de sondeo y bloqueo de tiempo

El modo de adquisición de bloqueo temporizado y sondeado se tryLockimplementa mediante métodos. En comparación con el modo de adquisición de bloqueo incondicional, tiene un mecanismo de recuperación de errores más completo.

Operación de adquisición de bloqueo interrumpible

lockInterruptibly El método puede mantener la respuesta a la interrupción mientras obtiene el bloqueo.

¡El código está debajo de Log! ! ! ! !

Se imprime el método lock (): Después de que el hilo 1 no pueda obtener el bloqueo, esperará a que se libere el bloqueo y no responderá a la interrupción.Cuando el hilo 0 libera el bloqueo, el hilo 1 reanuda la respuesta a la interrupción.

Thread-0:start get lock
Thread-0:already get lock
Thread-1:start get lock
Thread-0:working num 0
Thread-0:working num 1
Thread-0:working num 2
Thread-0:working num 3
Thread-0:working num 4
Thread-0:working num 5
Thread-0: release unlock
Thread-1:already get lock
Thread-1:Interrupt
Thread-1: release unlock

El método lockInterruptiblemente () imprime: El hilo 1 puede responder a la interrupción en el tiempo después de que no puede obtener el bloqueo.

Thread-0:start get lock
Thread-0:already get lock
Thread-1:start get lock
Thread-1:Interrupt
Thread-1: unlock failed
Thread-1: failed desc:null
Thread-0:working num 0
Thread-0:working num 1
Thread-0:working num 2
Thread-0:working num 3
Thread-0:working num 4
Thread-0:working num 5
Thread-0: release unlock

Ejecute el código de muestra lockInterruptiblyy lockla diferencia será clara .


    public static void main(String[] args) throws InterruptedException {
    
    
        LockTest lockTest = new LockTest();
        Thread t0 = new Thread(new Runnable(){
    
    
            @Override
            public void run() {
    
    
                lockTest.doWork();
            }
        });
        Thread t1 = new Thread(new Runnable(){
    
    
            @Override
            public void run() {
    
    
                lockTest.doWork();
            }
        });
        // 启动线程t1
        t0.start();
        Thread.sleep(10);
        // 启动线程t2
        t1.start();
        Thread.sleep(100);
        // 线程t1没有得到锁,中断t1的等待
        t1.interrupt();
    }

class LockTest {
    
    
    private Lock lock = new ReentrantLock();
    public void doWork() {
    
    
        String name = Thread.currentThread().getName();
        try {
    
    
            System.out.println(name + ":start get lock");
            //lock.lock();
            lock.lockInterruptibly();
            System.out.println(name + ":already get lock");
            for (int i = 0; i < 6; i++) {
    
    
                Thread.sleep(1000);
                System.out.println(name + ":working num "+ i);
            }
        } catch (InterruptedException e) {
    
    
            System.out.println(name + ":Interrupt");
        }finally{
    
    
            try {
    
    
                lock.unlock();
                System.out.println(name + ": release unlock");
            } catch (Exception e) {
    
    
                System.out.println(name + ": unlock failed");
                System.out.println(name + ": failed desc:" + e.getMessage());
            }

        }
    }
}

Consideraciones de rendimiento

Cuando se agrega Java 5 ReentrantLock, puede proporcionar un mejor rendimiento competitivo que los bloqueos integrados. Java 6 usa un algoritmo mejorado para administrar los bloqueos integrados, de modo que los bloqueos integrados ReentrantLockson casi iguales en rendimiento y su escalabilidad es básicamente la misma.

Justicia

Hay ReentrantLockdos opciones de equidad en el constructor: crear un bloqueo injusto (predeterminado) o un bloqueo equitativo.

  • Bloqueo justo: los subprocesos adquieren bloqueos en el orden en que realizan las solicitudes.
  • Bloqueo injusto: si el estado del bloqueo está disponible mientras el subproceso realiza una solicitud, el subproceso omitirá todos los subprocesos en espera y adquirirá el bloqueo.

En un bloqueo justo, si otro subproceso está reteniendo el bloqueo o si otro subproceso está esperando el bloqueo en la cola, el subproceso recién solicitado se colocará en la cola. En un bloqueo injusto, solo cuando el bloqueo está retenido por un hilo, el hilo recién solicitado se colocará en la cola.

¿Por qué no queremos que todos los candados sean justos?
Al realizar operaciones de bloqueo, la sobrecarga de utilizar bloqueos justos para suspender y reanudar subprocesos puede reducir considerablemente el rendimiento. Además, en situaciones reales, las garantías de equidad estadística (para garantizar que el hilo bloqueado pueda finalmente obtener el bloqueo) suelen ser suficientes, y la sobrecarga será mucho menor. Algunos se basan en algoritmos de colas justos para garantizar la corrección del negocio, pero estos algoritmos no son comunes. En la mayoría de los casos, el rendimiento de las cerraduras injustas es superior al de las cerraduras normales.

En synchronizedy ReentrantLockelige entre

ReentrantLockLa semántica proporcionada en las cerraduras y la memoria es la misma que la de las cerraduras integradas. Además, también proporciona algunas otras funciones: bloqueo de tiempo en espera, bloqueo interrumpible en espera, equidad y bloqueo no estructurado en bloque.

En comparación con las cerraduras explícitas, las cerraduras integradas todavía tienen grandes ventajas. El candado integrado es más familiar para los desarrolladores y es simple y compacto. ReentrantLockEl peligro es mayor que el mecanismo de sincronización, si te olvidas finallyde llamarlo en el bloque, se unlock()ha colocado una bomba de tiempo.

En algunos casos en los que el candado integrado no puede satisfacer las necesidades, se ReentrantLockpuede utilizar como una herramienta avanzada. Se debe utilizar cuando se necesitan algunas funciones avanzadas, entre las que se ReentrantLockincluyen: operaciones de adquisición temporizadas, sondeables e interrumpibles, colas justas y bloqueos de estructura sin bloques. De lo contrario, utilícelo primero synchronized.

Bloqueo de lectura y escritura

ReentrantLockSe implementa un bloqueo de mutex estándar: como máximo se mantiene un hilo cada vez ReentrantLock. Pero para mantener la integridad de los datos, la exclusión mutua suele ser una regla de bloqueo demasiado fuerte, que limita la concurrencia. La exclusión mutua es una estrategia de bloqueo conservadora. Aunque los conflictos pueden evitarse 写/写, los 写/读conflictos también pueden evitarse 读/读. Por lo tanto, si 读/读se relajan los requisitos de bloqueo de la situación, se mejorará el rendimiento del programa. En este caso, hay 读-写锁: se puede acceder a un recurso mediante varias operaciones de lectura o mediante una operación de escritura, pero no se pueden realizar las dos al mismo tiempo.

public interface ReadWriteLock {
    
    
    Lock readLock();
    Lock writeLock();
}

En 读-写锁la estrategia de bloqueo, se permiten múltiples operaciones de lectura al mismo tiempo, pero solo se permite una operación de escritura a la vez.

ReentrantReadWriteLockProporciona una semántica de bloqueo importante para ambos tipos de bloqueos. Y ReentrantLocksimilar,
ReentrantReadWriteLockcuando la construcción también puede optar por ser un candado justo sin bloqueo (predeterminado) o un bloqueo justo.

  • En un bloqueo justo, el hilo con el tiempo de espera más largo obtendrá el bloqueo primero. Si este bloqueo está retenido por un hilo de lectura y otro hilo solicita un bloqueo de escritura, ningún otro hilo de lector puede adquirir el bloqueo de lectura hasta que el hilo de escritura se agote y libere el bloqueo de escritura.
  • En un bloqueo injusto, el orden en el que los subprocesos obtienen permisos de acceso es incierto. Es posible degradar un hilo de escritor a un hilo de lector, pero no es posible actualizar de un hilo de lector a un hilo de escritor (esto provocará un punto muerto).

Ejemplo de código de bloqueo de lectura-escritura:

public class ReadWriteMap<K,V>{
    
    
    private final Map<K,V> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock read = lock.readLock();
    private final Lock write = lock.writeLock();
    public ReadWriteMap(Map<K,V> map){
    
    
        this.map = map;
    }
    public V put(K key,V value){
    
    
        write.lock();
        try {
    
    
            return map.put(key, value);
        }finally{
    
    
            write.unlock();
        }
    }
    public V get(Object key){
    
    
        read.lock();
        try {
    
    
            return map.get(key);
        }finally{
    
    
            read.unlock();
        }
    }
}

En comparación con la cerradura incorporada, la cerradura explícita proporciona algunas funciones ampliadas y tiene una mayor flexibilidad. Flexibilidad y mejor control de las filas de espera. Pero no ReentrantLockse puede reemplazar por completo synchronized. synchronizedDebe usarse sólo cuando no se pueda satisfacer la demanda.

读-写锁Permite que varios hilos de lectura accedan simultáneamente al objeto protegido. Al acceder a estructuras de datos dominadas por operaciones de lectura, puede mejorar la escalabilidad del programa.

Supongo que te gusta

Origin blog.csdn.net/lijie2664989/article/details/105739247
Recomendado
Clasificación