Explicación detallada de Lock en Java

1. Introducción

java.util.concurrent.locks.Lock es un mecanismo de sincronización de subprocesos similar a los bloques sincronizados . Pero Lock es más flexible que los bloques sincronizados. Lock es una interfaz y existe una clase de implementación llamada ReentrantLock.

2. La diferencia entre Lock y sincronizado

  • sincronizado es una palabra clave en el lenguaje Java. Lock es una interfaz.
  • sincronizado no requiere que el usuario libere manualmente el bloqueo, y el bloqueo se libera automáticamente cuando ocurre una excepción o el hilo finaliza; Lock requiere que el usuario libere el bloqueo manualmente. Si el bloqueo no se libera activamente, puede provocar un punto muerto.
  • El bloqueo se puede configurar con una estrategia justa para permitir que los subprocesos adquieran bloqueos en orden.
  • El método trylock se proporciona para intentar adquirir el bloqueo. Cuando el bloqueo se adquiere o no se puede adquirir, se devuelven diferentes valores de retorno para que el programa pueda manejarlo de manera flexible.
  • lock () y unlock () se pueden ejecutar en diferentes métodos, y el mismo hilo puede bloquear () en el método anterior y desbloquear () en otros métodos posteriores, lo cual es mucho más flexible que sincronizar.

3. Método abstracto de interfaz de bloqueo 

  • void lock(): adquiere el bloqueo. Si el bloqueo no está disponible, el subproceso actual se desactivará para fines de programación de subprocesos y se suspenderá hasta que se adquiera el bloqueo.
Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){
     
}finally{
    lock.unlock();   //释放锁
}
  • boolean tryLock(): devuelve verdadero inmediatamente si el bloqueo está disponible, devuelve falso inmediatamente si el bloqueo no está disponible;
  • boolean tryLock (tiempo prolongado, unidad TimeUnit) arroja InterruptedException: si el bloqueo está disponible, este método devuelve verdadero inmediatamente. Si el bloqueo no está disponible, el subproceso actual se deshabilitará para fines de programación de subprocesos y se suspenderá hasta que ocurra una de las tres cosas siguientes: ① el subproceso actual adquiere el bloqueo; ② el subproceso actual es interrumpido por otro subproceso y admite la adquisición de interrupciones. de bloqueos; ③ Si el bloqueo se adquiere después del tiempo de espera especificado, devuelve verdadero, si el bloqueo no se adquiere, devuelve falso.
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
         
     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

  • Desbloqueo vacío (): libera el bloqueo. La operación de liberar el bloqueo se realiza en el bloque final para garantizar que el bloqueo se libere definitivamente y evitar que se produzca un punto muerto.

四、ReentrantLock

El bloqueo de reentrada también se denomina bloqueo recursivo, 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. Para evitar problemas de interbloqueo, sincronizado también puede ser reentrante.

4.1 Prueba de reentrada sincronizada

public class ReentrantDemo {
    public synchronized  void method1() {
        System.out.println("synchronized method1");
        method2();
    }
    public synchronized void method2() {
        System.out.println("synchronized method2");
    }
    public static void main(String[] args) {
        ReentrantDemo reentrantDemo = new ReentrantDemo();
        reentrantDemo.method1();
    }
}

Resultados de la

Insertar descripción de la imagen aquí

4.2 Prueba de reentrada de ReentrantLock 

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 correctamente.

Insertar descripción de la imagen aquí

5. 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 varios 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 que el subproceso que obtuvo el bloqueo de escritura puede obtener el bloqueo de lectura, otros subprocesos no pueden obtener el bloqueo de lectura.

  • writeLock(): obtiene el bloqueo de escritura.
  • readLock(): obtiene el bloqueo de lectura.
    Ejecute tres subprocesos para realizar operaciones de lectura y escritura y configure una barrera. Después de que los subprocesos estén listos, esperan hasta adquirir el bloqueo. Cuando el tercer subproceso ejecuta cyclicBarrier.await();, la barrera se levanta y el Se ejecutan tres hilos al mismo tiempo.
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 1 adquiere el bloqueo de lectura primero, porque el bloqueo de lectura se puede compartir y todos los subprocesos 3 también pueden adquirir el bloqueo de lectura. Una vez que los subprocesos 1 y 3 completan la operación de lectura y liberan el bloqueo de lectura, el subproceso 2 puede adquirir el bloqueo de escritura y comience a realizar operaciones de escritura.

Insertar descripción de la imagen aquí

6. Bloqueo justo y bloqueo injusto 

  • Bloqueo justo: es muy justo. En un entorno concurrente, al adquirir un bloqueo, cada hilo primero verificará la cola de espera mantenida por este bloqueo. Si está vacía o el hilo actual es el primero en la cola de espera, lo hará. Ocupe el bloqueo; de lo contrario, se agregará a la cola de espera y se recuperará de la cola de acuerdo con las reglas FIFO.
  • Bloqueo injusto: es relativamente grosero, intentará ocupar directamente el bloqueo, si el intento falla, utilizará un método similar al bloqueo justo.

6.1 Cómo implementar 

  • ReentrantLock: el modo es un bloqueo injusto. También se pueden crear cerraduras justas mediante métodos de construcción;
public ReentrantLock() {
	sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • ReentrantReadWriteLock: el valor predeterminado es un bloqueo injusto, y también se puede crear un bloqueo justo mediante el método de construcción;
public ReentrantReadWriteLock() {
	this(false);
}
public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

6.2 Ventajas y desventajas

El rendimiento del bloqueo injusto es mayor que el rendimiento del bloqueo justo. En primer lugar, existe un retraso significativo entre la reanudación de un hilo suspendido y su ejecución. Además, los bloqueos injustos pueden aprovechar al máximo el intervalo de tiempo de la CPU y minimizar el tiempo de inactividad de la CPU.

7. Uso de la condición

Cuando se cumplen ciertas condiciones, se llama al método await() de Condición para hacer que el hilo actual entre en estado de suspensión y espere. Llame al método signalAll() de Condition para reactivar el hilo que entró en suspensión debido a await().

La sincronización de candados requiere que el usuario controle manualmente la adquisición y liberación del candado. Su flexibilidad permite una sincronización multiproceso más compleja y un mayor rendimiento, pero al mismo tiempo, el usuario debe capturar el proceso de ejecución del código a tiempo después de adquirir el candado. Excepción y liberación del bloqueo en el bloque de código finalmente.

Utilice bloqueos Lock y sus condiciones de sincronización para implementar un modelo productor-consumidor:

public class MessageStorageByLock {  
    private int maxSize;  
    private List<String> messages;  
  
    private final ReentrantLock lock;  
    private final Condition conditionWrite;//声明两个锁条件  
    private final Condition conditionRead;  
    public MessageStorageByLock(int maxSize) {  
        this.maxSize = maxSize;  
        messages = new LinkedList<String>();  
        lock = new ReentrantLock(true);//true修改锁的公平性,为true时,使用lifo队列来顺序获得锁  
        conditionWrite = lock.newCondition();//调用newCondition()方法,即new ConditionObject();  
        conditionRead = lock.newCondition();  
  
    }  
    public void set(String message){  
        //使用锁实现同步,获取所得操作,当锁被其他线程占用时,当前线程将进入休眠  
        lock.lock();  
        try{  
            while(messages.size() == maxSize){  
                    System.out.print("the message buffer is full now,start into wait()\n");  
                    conditionWrite.await();//满足条件时,线程休眠并释放锁。当调用 signalAll()时。线程唤醒并重新获得锁  
            }  
            Thread.sleep(100);  
            messages.add(message);  
            System.out.print("add message:"+message+" success\n");  
            conditionRead.signalAll();//唤醒因conditionRead.await()休眠的线程  
        }catch (InterruptedException e){  
            e.printStackTrace();  
        }finally {  
            lock.unlock();  
        }  
    }  
    public String get(){  
        String message = null;  
        lock.lock();  
        try{  
            while(messages.size() == 0){  
                conditionRead.await();  
                System.out.print("the message buffer is empty now,start into wait()\n");  
            }  
            Thread.sleep(100);  
            message = ((LinkedList<String>)messages).poll();  
            System.out.print("get message:"+message+" success\n");  
            conditionWrite.signalAll();  
        }catch (InterruptedException e){  
            e.printStackTrace();  
        }finally {  
            lock.unlock();  
        }  
        return message;  
    }  
}  
Modificador y tipo Método y descripción
vacío

cerrar con llave()

obtener bloqueo

vacío lockInterruptibly()

Adquiera el bloqueo a menos que se interrumpa el hilo actual   .

Condición newCondition()

Devuelve una nueva condición vinculada a esta instancia Lock.

booleano tryLock()

El candado sólo se puede adquirir cuando se llama.

booleano tryLock(long time, TimeUnit unit)

Si está inactivo dentro del tiempo de espera dado y el hilo actual no se ha  interrumpido, se adquiere el bloqueo.

vacío

desbloquear();

desbloquear bloqueo

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
 
public interface Lock {
    void lock();
 
    void lockInterruptibly() throws InterruptedException;
 
    boolean tryLock();
 
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
 
    void unlock();
 
    Condition newCondition();
}

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Date;
 
public interface Condition {
 
    void await() throws InterruptedException;
 
    void awaitUninterruptibly();
 
    long awaitNanos(long nanosTimeout) throws InterruptedException;
 
    boolean await(long time, TimeUnit unit) throws InterruptedException;
 
    boolean awaitUntil(Date deadline) throws InterruptedException;
 
    void signal();
 
    void signalAll();
}

8. Ejemplos de código

8.1 , Demo1 (primero demuestra la reentrada de la cerradura)

package com.szh.lock;
 
/**
 * 演示锁的可重入性
 */
public class Test01 {
 
    public synchronized void metthod1() {
        System.out.println("同步方法1");
        //线程执行 metthod1() 方法,默认 this 作为锁对象,
        //在 metthod1() 方法中调用了 method2() 方法,注意当前线程还是持有 this 锁对象的
        //method2() 同步方法默认的锁对象也是 this 对象, 要执行 method2() 必须先获得 this 锁对象,
        //当前 this 对象被当前线程持有,可以 再次获得 this 对象, 这就是锁的可重入性.
        //假设锁不可重入的话,可能会造成死锁
        method2();
    }
 
    public synchronized void method2() {
        System.out.println("同步方法2");
        method3();
    }
 
    public synchronized void method3() {
        System.out.println("同步方法3");
    }
 
    public static void main(String[] args) {
        Test01 obj=new Test01();
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                obj.metthod1();
            }
        }).start();
    }
}

resultado de la operación 

8.2 , Demo2 (uso básico de ReentrantLock)

package com.szh.lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * ReentrantLock 的基本使用
 */
public class Test02 {
 
    //定义一个显示锁
    static Lock lock=new ReentrantLock();
 
    public static void method() {
        //先获得锁
        lock.lock();
        //for循环此时就是同步代码块
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " ---> " + i);
        }
        //释放锁
        lock.unlock();
    }
 
    public static void main(String[] args) {
        Runnable r=new Runnable() {
            @Override
            public void run() {
                method();
            }
        };
        //启动三个线程
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

resultado de la operación

8.3 , Demo3 (use Lock para sincronizar bloques de código en diferentes métodos)

package com.szh.lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * 使用 Lock 锁同步不同方法中的同步代码块
 */
public class Test03 {
    //定义锁对象
    static Lock lock=new ReentrantLock();
 
    public static void method1() {
        //经常在 try 代码块中获得 Lock 锁, 在 finally 子句中释放锁
        try {
            lock.lock(); //获得锁
            System.out.println(Thread.currentThread().getName() + " ---method1--- " + System.currentTimeMillis());
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " ---method1--- " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock(); //释放锁
        }
    }
 
    public static void method2() {
        //经常在 try 代码块中获得 Lock 锁, 在 finally 子句中释放锁
        try {
            lock.lock(); //获得锁
            System.out.println(Thread.currentThread().getName() + " ---method2--- " + System.currentTimeMillis());
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " ---method2--- " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock(); //释放锁
        }
    }
 
    public static void main(String[] args) {
 
        Runnable r1=new Runnable() {
            @Override
            public void run() {
                method1();
            }
        };
 
        Runnable r2=new Runnable() {
            @Override
            public void run() {
                method2();
            }
        };
 
        new Thread(r1).start();
        new Thread(r1).start();
 
        new Thread(r2).start();
        new Thread(r2).start();
    }
}

resultado de la operación

8.4 , Demo4 (reentrante de bloqueo ReentrantLock) 

package com.szh.lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * ReentrantLock 锁的可重入性
 */
public class Test04 {
 
    static class SubThread extends Thread {
        //定义锁对象
        private static Lock lock=new ReentrantLock();
 
        //定义变量
        private static int num=0;
 
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                try {
                    //可重入锁指可以反复获得该锁
                    lock.lock();
                    lock.lock();
                    num++;
                }finally {
                    lock.unlock();
                    lock.unlock();
                }
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
 
        SubThread t1=new SubThread();
        SubThread t2=new SubThread();
 
        t1.start();
        t2.start();
 
        t1.join();
        t2.join();
 
        System.out.println(SubThread.num);
    }
}

resultado de la operación

8.5 , Demo5 (método lockInterruptively() de ReentrantLock)

package com.szh.lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * lockInterruptibly()方法
 *  如果当前线程未被中断则获得锁,
 *  如果当前线程被中断则出现异常.
 */
public class Test05 {
 
    static class Service {
        private Lock lock=new ReentrantLock(); //定义锁对象
        public void serviceMethod() {
            try {
                //lock.lock();  获得锁, 即使调用了线程的 interrupt() 方法, 也没有真正的中断线程
                //如果线程被中断了, 不会获得锁, 会产生异常
                lock.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " --- begin lock");
                //执行一段耗时的操作
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    new StringBuilder();
                }
                System.out.println(Thread.currentThread().getName() + " --- end lock");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " === 释放锁");
                lock.unlock(); //释放锁
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Service s=new Service();
 
        Runnable r=new Runnable() {
            @Override
            public void run() {
                s.serviceMethod();
            }
        };
 
        Thread t1=new Thread(r);
        t1.start();
        Thread.sleep(50);
 
        Thread t2=new Thread(r);
        t2.start();
        Thread.sleep(50);
 
        t2.interrupt(); //中断 t2 线程
    }
}

resultado de la operación 

8.6 , Demo6 (el método lockInterruptively() puede evitar el punto muerto) 

package com.szh.lock;
 
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * 通过 ReentrantLock 锁的 lockInterruptibly() 方法避免死锁的产生
 */
public class Test06 {
 
    static class MyLock implements Runnable {
 
        //创建两个ReentrantLock等锁对象
        private static ReentrantLock lock1=new ReentrantLock();
        private static ReentrantLock lock2=new ReentrantLock();
 
        int lockNum; //定义整数变量,决定使用哪个锁,偶数用lock1,奇数用lock2
 
        public MyLock(int lockNum) {
            this.lockNum=lockNum;
        }
 
        @Override
        public void run() {
            try {
                if (lockNum % 2 == 1) { //奇数, 先锁 1, 再锁 2
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁1,还需要获得锁2");
                    Thread.sleep(1000);
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2");
                }else { //偶数, 先锁 2, 再锁 1
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得了锁2,还需要获得锁1");
                    Thread.sleep(1000);
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2");
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (lock1.isHeldByCurrentThread()) { //判断当前线程是否持有该锁
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getName() + "线程退出");
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        MyLock myLock1=new MyLock(11);
        MyLock myLock2=new MyLock(22);
 
        Thread t1=new Thread(myLock1);
        Thread t2=new Thread(myLock2);
        t1.start();
        t2.start();
 
        //在 main 线程, 等待 3000 ms, 如果还有线程没有结束就中断该线程
        Thread.sleep(1000 * 3);
        //可以中断任何一个线程来解决死锁, t2 线程会放弃对锁 1 的申请, 同时释放锁 2, t1 线程会完成它的任务
        if (t2.isAlive()) {
            t2.interrupt();
        }
    }
}

resultado de la operación

8.7 , Demo7 (método tryLock (largo tiempo, unidad TimeUnit) de ReentrantLock) 

package com.szh.lock;
 
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * tryLock(long time, TimeUnit unit) 的作用在给定等待时长内,
 * 锁没有被另外的线程持有, 并且当前线程也没有被中断, 则获得该锁.
 * 通过该方法可以实现锁对象的限时等待.
 */
public class Test07 {
 
    static class TimeLock implements Runnable {
        private static ReentrantLock lock=new ReentrantLock(); //定义锁对象
 
        @Override
        public void run() {
            try {
                //假设 t1 线程先持有锁, 完成任务需要 4 秒钟,
                //这个时候 t2 线程尝试获得锁, t2 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
                if (lock.tryLock(3, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
                    Thread.sleep(1000 * 4);
                    /*
                        假设 t1 线程先持有锁, 完成任务需要 2 秒钟
                        这个时候t2 线程尝试获得锁, t2 线程会一直尝试
                        在它约定尝试的 3 秒内可以获得锁对象
                     */
                    //Thread.sleep(1000 * 2);
                }else {
                    System.out.println(Thread.currentThread().getName() + "没有获得锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }
 
    public static void main(String[] args) {
        TimeLock timeLock=new TimeLock();
 
        Thread t1=new Thread(timeLock);
        Thread t2=new Thread(timeLock);
 
        t1.setName("t1");
        t2.setName("t2");
 
        t1.start();
        t2.start();
    }
}

resultado de la operación 

8.8 , Demo8 (método tryLock() de ReentrantLock)

package com.szh.lock;
 
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * tryLock() 当锁对象没有被其他线程持有的情况下, 才会获得该锁定
 */
public class Test08 {
 
    static class Service {
        private ReentrantLock lock=new ReentrantLock();
 
        public void serviceMethod() {
            try {
                if (lock.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + "获得锁定");
                    Thread.sleep(1000 * 3); //模拟执行任务的时长
                }else {
                    System.out.println(Thread.currentThread().getName() + "没有获得锁定");
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Service service=new Service();
 
        Runnable r=new Runnable() {
            @Override
            public void run() {
                service.serviceMethod();
            }
        };
 
        Thread t1=new Thread(r);
        t1.start();
        Thread.sleep(100);
        Thread t2=new Thread(r);
        t2.start();
    }
}

resultado de la operación

8.9 , Demo9 (el método tryLock() puede evitar el punto muerto)

package com.szh.lock;
 
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * 使用 tryLock() 可以避免死锁
 */
public class Test09 {
 
    static class MyLock implements Runnable {
 
        private static ReentrantLock lock1=new ReentrantLock();
        private static ReentrantLock lock2=new ReentrantLock();
 
        private int lockNum;
 
        public MyLock(int lockNum) {
            this.lockNum=lockNum;
        }
 
        @Override
        public void run() {
            if (lockNum % 2 == 0) { //偶数先锁 1, 再锁 2
                while (true) {
                    try {
                        if (lock1.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得了锁1,还想获得锁2");
                            Thread.sleep(50);
                            try {
                                if (lock2.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2,完成任务了");
                                    return;
                                }
                            } finally {
                                if (lock2.isHeldByCurrentThread()) {
                                    lock2.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock1.isHeldByCurrentThread()) {
                            lock1.unlock();
                        }
                    }
                }
            }else { //奇数就先锁 2, 再锁 1
                while (true) {
                    try {
                        if (lock2.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得了锁2,还想获得锁1");
                            Thread.sleep(50);
                            try {
                                if (lock1.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2,完成任务了");
                                    return;
                                }
                            } finally {
                                if (lock1.isHeldByCurrentThread()) {
                                    lock1.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock2.isHeldByCurrentThread()) {
                            lock2.unlock();
                        }
                    }
                }
            }
        }
    }
 
    public static void main(String[] args) {
        MyLock lock1=new MyLock(11);
        MyLock lock2=new MyLock(22);
 
        Thread t1=new Thread(lock1);
        Thread t2=new Thread(lock2);
 
        t1.start();
        t2.start();
        //运行后, 使用 tryLock() 尝试获得锁, 不会傻傻的等待, 通过循环不停的再次尝试, 如果等待的时间足够长, 线程总是会获得想要的资源
    }
}

resultado de la operación 

Supongo que te gusta

Origin blog.csdn.net/m0_50370837/article/details/124471888
Recomendado
Clasificación