并发编程原理与实战(二十)ReentrantLock与Condition实战应用解析

上一篇讲解了Condition接口各个API的功能,本文继续对ReentrantLock+Condition的具体使用进行深入分析。

实现2个线程依次输出123456…

在系列文章的第二篇中,分析了采用synchronized与wait+notify实现一个经典的题目:如何控制2个线程依次输出1 2 3 4 5 6…?既然ReentrantLock可以代替synchronized,Condition可以代替wait+notify,可以实现更精细的并发协同控制,那么我们就来用ReentrantLock+Condition来实现这一个经典的题目。

ReentrantLock+Condition方案

我们回顾下这个题目的思路:

步骤1:创建2个线程t1,t2。

步骤2:t1运行输出1,t1停止输出(等待t2输出完,进入等待状态)。

步骤3:t2运行输出2,t2停止输出(等待t1输出完,进入等待状态)。

步骤4:循环执行步骤2和步骤3,即t1和t2依次执行”运行<->等待“动作。

这其中有两个等待,一个是“t1等待t2”,一个是“t2等待t1”,结合前面所学的Condition可以创建多个等待条件变量,可以创建两个Condition条件用来表示“t1等待t2”和“t2等待t1”。Condition提供了让线程进入等待状态的await()方法和唤醒其他线程的signal()方法,可以用来代替Object来的wait()方法和notify()方法。基于上面分析,我们对之前的代码改造如下:

public class ReentrantLockCollaboration {
   
   
    //输出数字
    private static int num = 1;
    //公共变量,线程执行标识
    private static boolean thread1Run = true;
    //锁对象
    private static final ReentrantLock lockObject = new ReentrantLock();
    // 线程1运行条件
    private static final Condition thread1RunCondition = lockObject.newCondition();
    // 线程2运行条件
    private static final Condition thread2RunCondition = lockObject.newCondition();

    public static void main(String[] args) throws InterruptedException {
   
   
        Thread t1 = new Thread(new Runnable() {
   
   
            @Override
            public void run() {
   
   
                while (num < 10) {
   
   
                    //获取锁对象
                    lockObject.lock();
                    try {
   
   
                        //判断是否轮到自己执行
                        while (!thread1Run) {
   
   
                            try {
   
   
                                //没到就进入等待
                                thread1RunCondition.await();
                            } catch (InterruptedException e) {
   
   
                                e.printStackTrace();
                            }
                        }
                        System.out.println(Thread.currentThread().getName() + " output " + num++);
                        thread1Run = false;
                        //执行完输出,通知t2执行
                        thread2RunCondition.signal();
                    } finally {
   
   
                        lockObject.unlock();
                    }
                }
            }
        }, "t1");

        Thread t2 = new Thread(new Runnable() {
   
   
            @Override
            public void run() {
   
   
                while (num < 10) {
   
   
                    //获取锁对象
                    lockObject.lock();
                    try {
   
   
                        //判断是否轮到自己执行
                        while (thread1Run) {
   
   
                            //没到就进入等待
                            try {
   
   
                                thread2RunCondition.await();
                            } catch (InterruptedException e) {
   
   
                                e.printStackTrace();
                            }
                        }
                        System.out.println(Thread.currentThread().getName() + " output " + num++);
                        thread1Run = true;
                        //执行完输出,通知t2执行
                        thread1RunCondition.signal();
                    } finally {
   
   
                        lockObject.unlock();
                    }
                }
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

运行结果:

t1 output 1
t2 output 2
t1 output 3
t2 output 4
t1 output 5
t2 output 6
t1 output 7
t2 output 8
t1 output 9
t2 output 10

结果符合我们的预期。

实现3个线程依次输出123456…

上面的例子中只有2个线程,一个线程输出完成后就唤醒另外的一个线程,现在我们对题目升级下:用3个线程依次输出1 2 3 4 5 6…那该如何实现?

按之前的方法我们对题目进行步骤拆解分析:

步骤1:创建3个线程t1,t2,t3。

步骤2:t1运行输出1,t1停止输出,进入等待状态(等待t2,t3输出完成)。

步骤3:t2运行输出2,t2停止输出,进入等待状态(等待t3,t1输出完成)。

步骤4:t3运行输出3,t3停止输出,进入等待状态(等待t1,t2输出完成)。

步骤5:循环执行步骤2、步骤3、步骤4,整个过程只有一个线程处于输出状态,另外两个线程处于等待状态。

<
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

帧栈

您的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值