使用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代码实现:

class ShareDataLock{
    // 线程执行的条件 1:线程1执行 2:线程2执行 3:线程3执行
    int number =1;
    // 锁
    Lock lock = new ReentrantLock();
    // 从锁中获得3个条件变量
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    // 第一个线程run之后执行的方法
    public void f1(){
        lock.lock();
        try {
            // 如果条件值不为1 就挂起等待
            while(number!=1){
                condition1.await();
            }
            // 故意阻塞100毫秒,看看其他的线程会不会不再排队
            Thread.sleep(100);
            System.out.println("------1--------");
            // 线程1 执行完毕 把变量设置为2
            number = 2;
            // 唤醒第2个条件变量
            condition2.signal();
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
            // 不管抛没抛出异常都要解锁,防止线程死锁
          lock.unlock();
        }
    }
    
    public void f2(){
        lock.lock();
        try {
            while(number!=2){
                condition2.await();
            }
            System.out.println("------2--------");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void f3(){
        lock.lock();
        try {
            while(number!=3){
                condition3.await();
            }
            System.out.println("------3--------");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


public class SynchronizedAndReentrantLockDemo {

    public static void main(String[] args) {
        ShareDataLock shareDataLock = new ShareDataLock();
        for (int i = 0; i < 10; i++) {
            // 3个线程分别执行1,2,3 3个方法 ,并且同时就绪
            new Thread(()->shareDataLock.f1(),"AA").start();
            new Thread(()->shareDataLock.f2(),"bb").start();
            new Thread(()->shareDataLock.f3(),"cc").start();
        }

    }
}

代码执行效果如下图:

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

四. 后话

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

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
在Android开发,正确处理多线程并发问题非常重要。以下是一些处理多线程并发问题的最佳实践: 1. 使用线程安全的数据结构:使用线程安全的数据结构可以在多个线程之间共享数据,而不会导致数据损坏或不一致。例如,可以使用ConcurrentHashMap代替HashMap,在多个线程之间进行读写操作。 2. 使用同步代码块:同步代码块可以确保在一个线程修改共享数据时,其他线程不会同时修改该数据。例如,可以使用synchronized关键字同步代码块来防止多个线程同时访问代码块的共享变量。 3. 使用锁:使用锁可以确保在一个线程修改共享数据时,其他线程不会同时修改该数据。例如,可以使用ReentrantLock类来实现锁。 4. 避免死锁:死锁是一种情况,在这种情况下,两个或多个线程互相等待对方释放资源,导致程序无法继续执行。为避免死锁,应该避免使用多个锁,或者确保锁获取的顺序是一致的。 5. 使用线程池:线程池可以管理和复用线程,避免了线程创建和销毁的开销。在Android开发,可以使用ThreadPoolExecutor或者AsyncTask等类来实现线程池。 6. 使用Handler:Handler是Android处理多线程的机制之一。使用Handler可以在主线程和其他线程之间进行通信,从而避免了一些并发问题。 最后,要时刻注意多线程并发的风险,确保代码没有竞态条件等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一一哥Sun

您的鼓励是我继续创作的动力哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值