【并发编程】concurrent包的Condition使用详解

3 篇文章 0 订阅
2 篇文章 0 订阅

最近使用Condition时有一些疑惑,于是自己做了几个实验,了解了Condition的具体用法,现记录如下。

       首先我们直接来看一段之前网上看到的一个输出1-9的例子,当然网上那个例子是有缺陷的,并不能保证每次都能输出1-9,后面会介绍原因,先看如下代码:

public class ConditionTest {

    public static void main(String[] args) {
        final AtomicInteger value = new AtomicInteger(1);
        final Lock lock = new ReentrantLock();
        final Condition reachThreeCondition = lock.newCondition();
        final Condition reachSixCondition = lock.newCondition();

        Thread threadA = new Thread(new Runnable() {
            public void run() {
                lock.lock();
                System.out.println("线程1拿到锁【1】");
                try {
                    // 输出1-3 之后激活reachThreeCondition
                    while (value.get() <= 3) {
                        System.out.println(value.get());
                        value.addAndGet(1);
                    }
                    reachThreeCondition.signal();
                    System.out.println("线程1【1】调用唤醒【reachThreeCondition】");
                } finally {
                    lock.unlock();
                    System.out.println("线程1【1】释放锁");
                }
                lock.lock();
                System.out.println("线程1拿到锁【2】");
                try {
                    // 挂起等待唤醒reachSixCondition操作则继续输出后续数字
                    System.out.println("线程1【2】进入等待");
                    reachSixCondition.await();
                    System.out.println("线程1【2】被唤醒");
                    // 输出剩余数字
                    while (value.get() <= 9) {
                        System.out.println(value.get());
                        value.addAndGet(1);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }

        });

        Thread threadB = new Thread(new Runnable() {
            public void run() {
                try {
                    lock.lock();
                    System.out.println("线程2拿到锁【1】");
                    while (value.get() <= 3) {
                        // 线程挂起等待输出1-3之后唤醒
                        System.out.println("线程2【1】进入等待");
                        reachThreeCondition.await();
                        System.out.println("线程2【1】被唤醒");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                    System.out.println("线程2【1】释放了锁");
                }
                try {
                    lock.lock();
                    System.out.println("线程2拿到锁【2】");
                    while (value.get() <= 6) {
                        System.out.println(value.get());
                        value.addAndGet(1);
                    }
                    // 唤醒reachSixCondition继续输出后续数字
                    reachSixCondition.signal();
                    System.out.println("线程2【2】唤醒了【reachSixCondition】");
                } finally {
                    lock.unlock();
                    System.out.println("线程2【2】释放了锁");
                }
            }

        });

        // 启动两个线程
        threadB.start();
        threadA.start();
    }
}

输出结果1:

线程2拿到锁【1】
线程21】进入等待
线程1拿到锁【11
2
3
线程11】调用唤醒【reachThreeCondition】
线程11】释放锁
线程1拿到锁【2】
线程12】进入等待
线程21】被唤醒
线程21】释放了锁
线程2拿到锁【24
5
6
线程22】唤醒了【reachSixCondition】
线程22】释放了锁
线程12】被唤醒
7
8
9

输出结果2:

线程2拿到锁【1】
线程21】进入等待
线程1拿到锁【11
2
3
线程11】调用唤醒【reachThreeCondition】
线程11】释放锁
线程21】被唤醒
线程21】释放了锁
线程2拿到锁【24
5
6
线程22】唤醒了【reachSixCondition】
线程22】释放了锁
线程1拿到锁【2】
线程12】进入等待

输出结果3:

线程1拿到锁【11
2
3
线程11】调用唤醒【reachThreeCondition】
线程11】释放锁
线程1拿到锁【2】
线程12】进入等待
线程2拿到锁【1】
线程21】释放了锁
线程2拿到锁【24
5
6
线程22】唤醒了【reachSixCondition】
线程22】释放了锁
线程12】被唤醒
7
8
9

       上面列出了三种输出结果,结果1其实是一个很理想的运行结果,所有代码都执行完了,其实读者可以反复运行上面代码,会发现可能出现好几种结果,这里只列出三种典型的结果。
       我们来根据结果做如下分析:
1. 线程1和线程2同时启动,此时首次出现锁竞争,上述结果1是线程2获取到锁(此处也可能是线程1获取到锁,见结果3),则线程1处于挂起状态,等待获取锁,线程2获取锁后判断满足条件,则使用reachThreeCondition.await()让线程进入挂起状态,且此处会释放锁
2. 由于上一步线程2释放了锁,所以此时线程1可获取到锁,于是线程1获取到锁,并继续执行输出123,之后调用reachThreeCondition.signal()唤醒线程2,此时线程1和线程2同时运行,线程1和2分别继续执行后续释放锁的代码(此时线程2其实没有锁可释放)。
3. 此时又会出现锁竞争,线程1和线程2会竞争锁(结果1和结果3是由线程1拿到了锁,结果2是由线程2拿到了锁),此时分两种情况:
a.(1)线程1拿到锁,之后满足条件通过reachSixCondition挂起并释放锁,(2)此时线程2拿到锁输出456之后唤醒reachSixCondition,之后俩线程继续各自执行后续代码到程序结束,完整输出1-9.
b.线程2拿到锁,之后不满足条件释放锁,此时第三次出现锁竞争,会出现两种结果,1)线程1拿到锁则执行a逻辑,完整输出1-9;2)线程2拿到锁,则执行线程2输出4-6,之后释放锁,线程1获取锁,之后执行reachSixCondition挂起,由于此时线程2已执行结束,则线程1的reachSixCondition没有地方被唤醒,所以线程1会一直处于挂起状态。

所以通过上面跟踪代码,理解了Condition的使用,其实在于可以根据不同条件来挂起、唤醒线程,且会自动释放锁,以达到可以更加方便的实现线程的有序性


欢迎关注个人博客:blog.scarlettbai.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值