[Java] Programación concurrente JUC-Bloqueo de bloqueo

I. Descripción general

Lock es un mecanismo de sincronización de subprocesos similar a Synchronized, pero Lock es más flexible que Synchronized. Lock es una interfaz con dos clases de implementación: ReentrantLock (bloqueo de reentrante), ReentrantReadWriteLock (bloqueo de lectura y escritura)

Dos, la diferencia entre Lock y Synchronized

  1. lock es una interfaz, sincronizado es una palabra clave en Java
  2. Sincronizado no requiere que el usuario libere manualmente el bloqueo y lo libera automáticamente cuando ocurre una excepción o el hilo finaliza; el bloqueo requiere que el usuario libere manualmente el bloqueo. Si el bloqueo no se libera activamente, puede provocar un punto muerto.
  3. El bloqueo puede hacer que el hilo que espera el bloqueo responda a la interrupción. Cuando se usa sincronizado, el hilo en espera esperará para siempre y no podrá responder a la interrupción.
    inserte la descripción de la imagen aquí

3. Bloquear API

inserte la descripción de la imagen aquí

Cuatro, ReentrantLock (bloqueo de reentrada), ReentrantReadWriteLock (bloqueo de lectura y escritura)

1. ReentrantLock (bloqueo reentrante)

Los bloqueos reentrantes también se denominan bloqueos recursivos, lo que significa que después de que la función externa del mismo subproceso adquiere el bloqueo, la función recursiva interna todavía tiene el código para adquirir el bloqueo, pero no se ve afectada.

public class ReentrantDemo implements Runnable {
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        set();
    }
    public void set() {
    
    
        try {
    
    
            lock.lock();
            System.out.println("set 方法");
            get();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();// 必须在finally中释放
        }
    }

    public void get() {
    
    

        try {
    
    
            lock.lock();
            System.out.println("get 方法");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public static void main(String[] args) {
    
    
        ReentrantDemo reentrantDemo = new ReentrantDemo();
        new Thread(reentrantDemo).start();
    }

Resultados de la prueba: el mismo hilo primero adquiere el bloqueo en el método set, luego llama al método get y adquiere repetidamente el mismo bloqueo en el método get. Ambos métodos se ejecutan con éxito.
inserte la descripción de la imagen aquí

2. ReentrantReadWriteLock (bloqueo de lectura y escritura)

Los bloqueos de lectura y escritura pueden adquirir bloqueos de lectura o bloqueos de escritura, respectivamente. Es decir, las operaciones de lectura y escritura de datos se separan y dividen en dos bloqueos que se asignarán a los subprocesos, de modo que varios subprocesos puedan realizar operaciones de lectura al mismo tiempo. Los bloqueos de lectura usan el modo compartido; los bloqueos de escritura usan el modo exclusivo; los bloqueos de lectura pueden ser mantenidos por múltiples subprocesos al mismo tiempo cuando no hay un bloqueo de escritura y los bloqueos de escritura son exclusivos. Cuando hay un bloqueo de lectura, no se puede obtener el bloqueo de escritura; y cuando hay un bloqueo de escritura, excepto el hilo que obtiene el bloqueo de escritura puede obtener el bloqueo de lectura, otros hilos no pueden obtener el bloqueo de lectura

  • writeLock(): obtiene un bloqueo de escritura.
  • readLock(): adquiere un bloqueo de lectura.

Ejecute tres subprocesos para operaciones de lectura y escritura y establezca una barrera. Después de que los subprocesos estén listos, esperan antes de adquirir el bloqueo. Cuando el tercer subproceso ejecuta
cyclicBarrier.await(), la barrera se libera y los tres subprocesos se ejecutan. simultáneamente.

public class WriteAndReadLockTest {
    
    
    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10,
            60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    private static int i = 100;
    public static void main(String[] args) {
    
    
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            write(Thread.currentThread());
        });
        threadPoolExecutor.execute(()->{
    
    
            read(Thread.currentThread());
        });
        threadPoolExecutor.shutdown();
    }

    private static void read(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.readLock().lock();
        try {
    
    
            System.out.println("读线程 "+ thread.getName() + " 开始执行, i=" + i);
            Thread.sleep(1000);
            System.out.println(thread.getName() +" is over!");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            reentrantReadWriteLock.readLock().unlock();

        }
    }
    private static void write(Thread thread) {
    
    
        try {
    
    
            cyclicBarrier.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
    
    
            e.printStackTrace();
        }
        reentrantReadWriteLock.writeLock().lock();
        try {
    
    
            i++;
            System.out.println("写线程 "+ thread.getName() + " is doing, i=" + i);
            System.out.println(thread.getName() +" is over!");
        } finally {
    
    
            reentrantReadWriteLock.writeLock().unlock();
        }
    }
}

Resultado de la ejecución: el subproceso 3 adquiere el bloqueo de lectura primero. Debido a que el bloqueo de lectura se puede compartir, el subproceso 1 también puede adquirir el bloqueo de lectura. Una vez completadas las operaciones de lectura del subproceso 1 y el subproceso 3, el bloqueo de lectura se puede liberar antes que el subproceso 2. puede adquirirlo al bloqueo de escritura y comenzar la operación de escritura.
inserte la descripción de la imagen aquí

5. Implementación del código API de Lock lock

1、bloquear()、desbloquear()

public class LockAndUnlock  implements Runnable{
    
    
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
    
    
        //上锁
        lock.lock();
        try {
    
    
            System.out.println("ThreadA获取锁");
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock(); // 解锁
            System.out.println("ThreadA释放锁");
        }
    }

    public static void main(String[] args) {
    
    
        LockAndUnlock lockAndUnlock = new LockAndUnlock();
        Thread threadA = new Thread(lockAndUnlock,"threadA");
        threadA.start();
    }
}

Resultados de:
inserte la descripción de la imagen aquí

2、bloquear de forma interrumpible()

Ejemplo: cuando dos subprocesos quieren adquirir un bloqueo a través de lock.lockInterruptfully() al mismo tiempo, si el subproceso A adquiere el bloqueo en este momento y el subproceso B solo está esperando, entonces llamar al método threadB.interrupt() en el subproceso B puede interrumpir el proceso de espera del hilo B.

public class LockInterruptiblyDemo {
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadA获得锁 ");
                    // 模拟线程A持有锁的一段时间
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadA在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lockInterruptibly();
                    System.out.println("ThreadB获得锁");
                } catch (InterruptedException e) {
    
    
                    System.out.println("ThreadB在等待锁时被中断");
                } finally {
    
    
                    if (Thread.holdsLock(lock)) {
    
    
                        lock.unlock();
                    }
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();

        // 在线程B等待锁的过程中,中断线程B
        threadB.interrupt();
    }
}

Resultados de:
inserte la descripción de la imagen aquí

3、tryLock()

Ejemplo: cuando dos subprocesos quieren adquirir un candado a través de lock.tryLock() al mismo tiempo, si el subproceso A adquiere el candado en este momento, el subproceso B no esperará y dejará de adquirir el candado directamente.

public class TryLock{
    
    
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
    
    
        Thread threadA = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadA获得了锁");
                        // 模拟线程A持有锁的一段时间
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadA获取锁失败");
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (lock.tryLock()) {
    
    
                    try {
    
    
                        System.out.println("ThreadB获得了锁");
                    } finally {
    
    
                        lock.unlock();
                    }
                } else {
    
    
                    System.out.println("ThreadB获取锁失败");
                }
            }
        });

        threadA.start();
        // 让线程A先获取到锁
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadB.start();
    }
}

Resultados de:
inserte la descripción de la imagen aquí

4、tryLock(mucho tiempo, unidad TimeUnit)

Este método devuelve inmediatamente el valor verdadero si el bloqueo está disponible.
Si el bloqueo no está disponible, el subproceso actual se desactiva para la programación de subprocesos y se suspende hasta que suceda una de las tres cosas siguientes:
● El subproceso actual adquiere el bloqueo.
● Algún otro hilo interrumpe el hilo actual.
● tiempo de espera transcurrido, devuelve falso

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
                Thread.sleep(1000 * 4);
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (Thread.holdsLock(lock)) {
    
    
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();
    }
}

Resultados de:
inserte la descripción de la imagen aquí

Si el bloqueo no está disponible, el subproceso actual se desactivará para la programación de subprocesos y permanecerá inactivo hasta que ocurra una de las tres situaciones siguientes: El subproceso actual puede interrumpirse.

public class TryLockParam implements Runnable{
    
    
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        try {
    
    
            //假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
            //这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                System.out.println(Thread.currentThread().getName() + "获得锁");
                for (int i = 0; i <= 500000000; i++) {
    
    
                    if (i == 500000000) {
    
    
                        System.out.println("运算结束");
                    }
                }
                lock.unlock();
            }else {
    
    
                System.out.println(Thread.currentThread().getName() + "放弃获得锁");
            }
        } catch (InterruptedException e) {
    
    
            System.out.println(Thread.currentThread().getName() +"中断");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
    
    
        TryLockParam tryLockParam=new TryLockParam();

        Thread threadA=new Thread(tryLockParam,"threadA");
        Thread threadB=new Thread(tryLockParam,"threadB");

        threadA.start();
        threadB.start();


        try {
    
    
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadA.interrupt();
        threadB.interrupt();
    }
}

Resultado de la ejecución: el subproceso A adquiere el bloqueo, otros subprocesos duermen y luego se interrumpen
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_45490023/article/details/132076149
Recomendado
Clasificación