Explication détaillée de Lock en Java

1. Introduction

java.util.concurrent.locks.Lock est un mécanisme de synchronisation de threads similaire aux blocs synchronisés . Mais Lock est plus flexible que les blocs synchronisés. Lock est une interface et il existe une classe d'implémentation appelée ReentrantLock.

2. La différence entre Lock et synchronisé

  • synchronisé est un mot-clé du langage Java. Le verrou est une interface.
  • synchronisé ne nécessite pas que l'utilisateur libère manuellement le verrou, et le verrou est automatiquement libéré lorsqu'une exception se produit ou que le thread se termine ; Lock nécessite que l'utilisateur libère manuellement le verrou. Si le verrou n'est pas activement libéré, cela peut entraîner un impasse.
  • Le verrouillage peut être configuré avec une stratégie équitable pour permettre aux threads d'acquérir les verrous dans l'ordre.
  • La méthode trylock est fournie pour tenter d'acquérir le verrou.Lorsque le verrou est acquis ou ne peut pas être acquis, différentes valeurs de retour sont renvoyées afin que le programme puisse le gérer de manière flexible.
  • lock() et unlock() peuvent être exécutés dans différentes méthodes, et le même thread peut lock() dans la méthode précédente et unlock() dans d'autres méthodes suivantes, ce qui est beaucoup plus flexible que synchronisé.

3. Méthode abstraite de l'interface de verrouillage 

  • void lock() : Acquérir le verrou. Si le verrou n'est pas disponible, le thread actuel sera désactivé à des fins de planification des threads et dormira jusqu'à ce que le verrou soit acquis.
Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){
     
}finally{
    lock.unlock();   //释放锁
}
  • boolean tryLock() : renvoie true immédiatement si le verrou est disponible, renvoie false immédiatement si le verrou n'est pas disponible ;
  • boolean tryLock (long time, unité TimeUnit) lève InterruptedException : si le verrou est disponible, cette méthode renvoie true immédiatement. Si le verrou n'est pas disponible, le thread actuel sera désactivé à des fins de planification des threads et dormira jusqu'à ce que l'une des trois choses suivantes se produise : ① le thread actuel acquiert le verrou ; ② le thread actuel est interrompu par un autre thread et prend en charge l'acquisition d'interruption. de verrous ; ③ Si le verrou est acquis après le temps d'attente spécifié, il renvoie vrai, si le verrou n'est pas acquis, il renvoie faux.
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
         
     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

  • void unlock() : relâchez le verrou. L'opération de libération du verrou est effectuée dans le bloc final pour garantir que le verrou est définitivement libéré et éviter qu'un blocage ne se produise.

四、ReentrantLock

Le verrouillage de réentrée est également appelé verrouillage récursif, ce qui signifie qu'une fois que la fonction externe du même thread a acquis le verrou, la fonction récursive interne a toujours le code pour acquérir le verrou, mais il n'est pas affecté. Pour éviter les problèmes de blocage, la synchronisation peut également être réentrante.

4.1. Test de réentrée synchronisé

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();
    }
}

Résultats du

Insérer la description de l'image ici

4.2. Test de réentrée 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();
    }
}

Résultats du test : le même thread acquiert d'abord le verrou dans la méthode set, puis appelle la méthode get et acquiert à plusieurs reprises le même verrou dans la méthode get. Les deux méthodes s'exécutent avec succès.

Insérer la description de l'image ici

5. ReentrantReadWriteLock (verrouillage en lecture-écriture) 

Les verrous en lecture-écriture peuvent respectivement acquérir des verrous en lecture ou des verrous en écriture. C'est-à-dire que les opérations de lecture et d'écriture des données sont séparées et divisées en deux verrous à allouer aux threads, afin que plusieurs threads puissent effectuer des opérations de lecture en même temps. Les verrous en lecture utilisent le mode partagé ; les verrous en écriture utilisent le mode exclusif ; les verrous en lecture peuvent être détenus par plusieurs threads en même temps lorsqu'il n'y a pas de verrou en écriture et les verrous en écriture sont exclusifs. Lorsqu'il y a un verrou en lecture, le verrou en écriture ne peut pas être obtenu ; et lorsqu'il y a un verrou en écriture, sauf que le thread qui a obtenu le verrou en écriture peut obtenir le verrou en lecture, les autres threads ne peuvent pas obtenir le verrou en lecture.

  • writeLock() : Obtenez le verrou en écriture.
  • readLock() : Obtenez le verrou de lecture.
    Exécutez trois threads pour effectuer des opérations de lecture et d'écriture et établissez une barrière. Une fois que les threads sont prêts à tour de rôle, ils attendent d'acquérir le verrou. Lorsque le troisième thread exécute cyclicBarrier.await();, la barrière est levée et le trois threads s'exécutent en même temps.
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();
        }
    }
}

 Résultat de l'exécution : Le thread 1 acquiert le verrou de lecture en premier, car le verrou de lecture peut être partagé et tous les threads 3 peuvent également acquérir le verrou de lecture. Une fois que les threads 1 et 3 ont terminé l'opération de lecture et libéré le verrou de lecture, le thread 2 peut acquérir le verrouillage d'écriture et commencez à effectuer des opérations d'écriture.

Insérer la description de l'image ici

6. Verrouillage équitable et verrouillage injuste 

  • Fair lock : C'est très juste. Dans un environnement concurrent, lors de l'acquisition d'un verrou, chaque thread vérifiera d'abord la file d'attente maintenue par ce verrou. Si elle est vide, ou si le thread actuel est le premier dans la file d'attente, il le fera. occupez le verrou, sinon il sera ajouté à la file d'attente et sera récupéré de la file d'attente selon les règles FIFO.
  • Verrouillage injuste : il est relativement grossier. Il tentera directement d'occuper le verrou. Si la tentative échoue, il utilisera une méthode similaire au verrouillage équitable.

6.1. Comment mettre en œuvre 

  • ReentrantLock : le mode est un verrouillage injuste. Des serrures équitables peuvent également être créées grâce à des méthodes de construction ;
public ReentrantLock() {
	sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • ReentrantReadWriteLock : la valeur par défaut est un verrou injuste, et un verrou équitable peut également être créé via la méthode de construction ;
public ReentrantReadWriteLock() {
	this(false);
}
public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

6.2 . Avantages et inconvénients

Les performances de verrouillage injustes sont supérieures aux performances de verrouillage équitables. Premièrement, il existe un délai important entre la reprise d’un thread suspendu et son exécution réelle. De plus, des verrouillages injustes peuvent exploiter davantage la tranche de temps du processeur et minimiser le temps d'inactivité du processeur.

7. Utilisation de la condition

Lorsque certaines conditions sont remplies, la méthode wait() de Condition est appelée pour que le thread actuel entre en état de veille et attende. Appelez la méthode signalAll() de Condition pour réveiller le thread qui s'est endormi à cause de wait().

La synchronisation du verrou nécessite que l'utilisateur contrôle manuellement l'acquisition et la libération du verrou.Sa flexibilité permet une synchronisation multithread plus complexe et des performances plus élevées, mais en même temps, l'utilisateur doit capturer le processus d'exécution du code à temps après l'acquisition du verrou. Exception et relâchez le verrou dans le bloc de code final.

Utilisez les verrous Lock et leurs conditions de synchronisation pour implémenter un modèle producteur-consommateur :

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;  
    }  
}  
Modificateur et type Méthode et description
vide

verrouillage()

obtenir un verrou

vide lockInterruptibly()

Acquérir le verrou à moins que le thread actuel ne soit  interrompu  .

Condition newCondition()

Renvoie une nouvelle condition liée à cette instance Lock.

booléen tryLock()

Le verrou ne peut être acquis que sur appel.

booléen tryLock(long time, TimeUnit unit)

S'il est inactif pendant le temps d'attente donné et que le thread en cours n'a pas été  interrompu, le verrou est acquis.

vide

ouvrir();

déverrouiller le verrou

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. Exemples de codes

8.1 , Demo1 (démontrer d'abord la réentrance du verrou)

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();
    }
}

résultat de l'opération 

8.2 , Demo2 (utilisation de base 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();
    }
}

résultat de l'opération

8.3 , Demo3 (utilisez Lock pour synchroniser les blocs de code dans différentes méthodes)

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();
    }
}

résultat de l'opération

8.4 , Demo4 (réentrance du verrouillage 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);
    }
}

résultat de l'opération

8.5 , Demo5 (méthode lockInterruptably() 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 线程
    }
}

résultat de l'opération 

8.6 , Demo6 (la méthode lockInterruptably() peut éviter une impasse) 

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();
        }
    }
}

résultat de l'opération

8.7 , Demo7 (méthode tryLock (long time, TimeUnit unit) 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();
    }
}

résultat de l'opération 

8.8 , Demo8 (méthode 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();
    }
}

résultat de l'opération

8.9 , Demo9 (la méthode tryLock() peut éviter une impasse)

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() 尝试获得锁, 不会傻傻的等待, 通过循环不停的再次尝试, 如果等待的时间足够长, 线程总是会获得想要的资源
    }
}

résultat de l'opération 

Je suppose que tu aimes

Origine blog.csdn.net/m0_50370837/article/details/124471888
conseillé
Classement