• 如何使用ReentrantLock的条件变量,让多个线程顺序执行?


    一. 前言

    近日耀哥的一个学生在参加某公司校招面试时,遇到一个多个线程顺序执行的面试题,特意记录下来和大家分享一下,这个题目的具体要求是这样的:

    假设有3个线程 a,b,c,要求三个线程一起进入到就绪态,执行时一定按照 a-->b-->c的顺序执行。即使a或者b线程进入到了阻塞态,也一定按照a-->b-->c的顺序运行线程。请问该如何保证实现这个需求呢?

    二. 解决方案

    关于这道题,耀哥百度一下网上常见的实现思路,大致有4种解决方案:

    1. 通过join()方法使当前线程“阻塞”,等待指定线程执行完毕后继续执行;

    2. 通过倒数计时器CountDownLatch实现;

    3. 通过创建单一化线程池 newSingleThreadExecutor()实现;

    4. 通过ReentrantLock 中的条件变量实现;

    今天耀哥先使用ReentrantLock 的条件变量来实现这个题目中的需求。

    三. 使用ReentrantLock 条件变量

    首先咱们来了解一下,什么是ReentrantLock 条件变量(Condition)。

    ReentrantLock 中的条件变量功能,类似于普通 synchronized 的 wait、notify,我们可以使用Reentrantlock 锁,配合 Condition 对象上的 await()和 signal()或 signalAll()方法,来实现线程间协作。与synchronized的wait和notify不同之处在于,ReentrantLock中的条件变量可以有多个,可以实现更精细的控制线程。

    Condition中常用的方法API有如下这些:

    ReentrantLock代码实现:

    1. class ShareDataLock{
    2.     // 线程执行的条件 1:线程1执行 2:线程2执行 3:线程3执行
    3.     int number =1;
    4.     // 锁
    5.     Lock lock = new ReentrantLock();
    6.     // 从锁中获得3个条件变量
    7.     Condition condition1 = lock.newCondition();
    8.     Condition condition2 = lock.newCondition();
    9.     Condition condition3 = lock.newCondition();
    10.     // 第一个线程run之后执行的方法
    11.     public void f1(){
    12.         lock.lock();
    13.         try {
    14.             // 如果条件值不为1 就挂起等待
    15.             while(number!=1){
    16.                 condition1.await();
    17.             }
    18.             // 故意阻塞100毫秒,看看其他的线程会不会不再排队
    19.             Thread.sleep(100);
    20.             System.out.println("------1--------");
    21.             // 线程1 执行完毕 把变量设置为2
    22.             number = 2;
    23.             // 唤醒第2个条件变量
    24.             condition2.signal();
    25.         } catch (Exception e) {
    26.           e.printStackTrace();
    27.         } finally {
    28.             // 不管抛没抛出异常都要解锁,防止线程死锁
    29.           lock.unlock();
    30.         }
    31.     }
    32.     
    33.     public void f2(){
    34.         lock.lock();
    35.         try {
    36.             while(number!=2){
    37.                 condition2.await();
    38.             }
    39.             System.out.println("------2--------");
    40.             number = 3;
    41.             condition3.signal();
    42.         } catch (Exception e) {
    43.             e.printStackTrace();
    44.         } finally {
    45.             lock.unlock();
    46.         }
    47.     }
    48.     
    49.     public void f3(){
    50.         lock.lock();
    51.         try {
    52.             while(number!=3){
    53.                 condition3.await();
    54.             }
    55.             System.out.println("------3--------");
    56.             number = 1;
    57.             condition1.signal();
    58.         } catch (Exception e) {
    59.             e.printStackTrace();
    60.         } finally {
    61.             lock.unlock();
    62.         }
    63.     }
    64. }
    65. public class SynchronizedAndReentrantLockDemo {
    66.     public static void main(String[] args) {
    67.         ShareDataLock shareDataLock = new ShareDataLock();
    68.         for (int i = 0; i < 10; i++) {
    69.             // 3个线程分别执行1,2,3 3个方法 ,并且同时就绪
    70.             new Thread(()->shareDataLock.f1(),"AA").start();
    71.             new Thread(()->shareDataLock.f2(),"bb").start();
    72.             new Thread(()->shareDataLock.f3(),"cc").start();
    73.         }
    74.     }
    75. }

    代码执行效果如下图:

    现在我们就会发现,3个线程已经可以被随意控制了,你会了吗?

    四. 后话

    如上文所述,让多个线程按顺序执行,网上常见的解决方案有4种。但大家要注意的是,面试官出这个题有一个先决条件,“要让所有的线程同时就绪,所以我们就可以排除使用join方法和使用单一化线程池的方案了。那么要想实现这个面试题中的需求,比较靠谱的方法只剩下ReentrantLock 中的条件变量和使用倒数计时器CountDownLatch两种方案了今天咱们暂时先介绍条件变量的方法,耀哥在日后的文章中介绍怎样使用CountDownLatch让多个线程有序执行,敬请各位粉丝儿们继续期待哦。

  • 相关阅读:
    LogUtils工具类
    基于hydra库实现yaml配置文件的读取(支持命令行参数)
    简单说说量化交易接口有哪些用途?
    前端使用 Konva 实现可视化设计器(15)- 自定义连接点、连接优化
    XSS原理
    调度算法1
    彩色文本进度条
    嵌入式分享合集55
    微信小程序获取用户头像调整
    linux企业级常用服务搭建
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/127731468