前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >8.JUC线程高级-Condition和线程顺序执行

8.JUC线程高级-Condition和线程顺序执行

作者头像
用户1212940
发布2022-04-13 15:05:44
2480
发布2022-04-13 15:05:44
举报
文章被收录于专栏:LambdaLambda

有的时候我们希望线程按照希望的顺序依次执行,比如线程A,B,C,按照顺序依次执行,这时候就要用到阻塞和唤醒,之前的时候我们学到过wait()nofity/notifyAll()这两个方法,这里我们使用java.concurrent.locks.Lock接口来实现类似的功能;

用到的包和类

代码语言:javascript
复制
java.concurrent.locks.Lock:接口
|-->java.concurrent.locks.ReentrantLock:实现类
|-->java.util.concurrent.locks.Condition:抽象类

方法:

代码语言:javascript
复制
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

要求

  1. 创建一个TestAlternate类,有三个方法loopA(),loopB(),loopC(),分别打印A,B,C
  2. 主函数中创建三个线程,绑定三个匿名类实现Runnable接口
  3. 主函数中循环10次,使得每次打印都按照A–>B–>C的顺序来打印

创建类

TestAlternate.java

代码语言:javascript
复制
class TestAlternate{
    //线程执行顺序标记,1:表示loopA执行,2:表示loopB执行,3:表示loopC执行
    private volatile int number = 1;
    //获得lock锁
    private Lock lock = new ReentrantLock();
    //创建三个condition对象用来await(阻塞)和signal(唤醒)指定的线程
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    protected void loopA(){
        lock.lock();//上锁
        try {

          /*如果不是第一个标志位,就阻塞,为了解决虚假唤醒问题,使用while关键字
          */
            while(number!=1){
                try {
                    c1.await();//阻塞类似wait()
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"-A");
            number = 2;//使能第二个方法
            c2.signal();//唤醒第二个线程,类似notify()方法

        } finally {
            lock.unlock();//解锁
        }

    }

    protected void loopB(){
        lock.lock();//上锁
        try {

          //如果不是第一个标志位,就阻塞
            while(number!=2){
                try {
                    c2.await();//阻塞类似wait()
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"-B");
            number = 3;//使能第3个方法
            c3.signal();//唤醒第三个线程,类似notify()方法

        } finally {
            lock.unlock();//解锁
        }
    }
    protected void loopC(){
        lock.lock();//上锁
        try {

          //如果不是第一个标志位,就阻塞
            while(number!=3){
                try {
                    c3.await();//阻塞类似wait()
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"-C");
            number = 1;//使能第1个方法
            c1.signal();//唤醒第一个线程,类似notify()方法

        } finally {
            lock.unlock();//解锁
        }
    }
}

测试输出:

代码语言:javascript
复制
loopA0-A
loopB0-B
loopC0-C
loopA1-A
loopB1-B
loopC1-C
loopC2-C//虚假唤醒问题
loopA2-A
loopB2-B

虚假唤醒的注意事项

出现虚假唤醒的原因: 假如A1A2两个线程争夺loopA,A2夺得了cpu执行权,结果发现此时A2的标记为number不是1,于是await,A2开始阻塞这个时候释放锁和资源,然后B,C线程得到cpu执行权按照顺序执行完毕,此时A的标志位是1,此时A1和A2的锁都是c2.await()A1,A2同时被被唤醒A1抢到了cpu执行权,打印输出loopA,并改变number为2,然后由于A2也被唤醒,但是由于是if语句,在阻塞前只判断了一次,即便此时number不是2了,但是A2不会再次判断number的值,继续往下执行,导致重复输出loopA解决方案:if替换为while,使得每次都判断number的值是否正确,保证了程序的正常运行,避免虚假唤醒的情况出现。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-09-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用到的包和类
  • 要求
  • 创建类
    • 虚假唤醒的注意事项
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档