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

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

用到的包和类

java.concurrent.locks.Lock:接口
|-->java.concurrent.locks.ReentrantLock:实现类
|-->java.util.concurrent.locks.Condition:抽象类

方法:

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

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();//解锁
        }
    }
}

测试输出:

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的值是否正确,保证了程序的正常运行,避免虚假唤醒的情况出现。

你可能感兴趣的:(8.JUC线程高级-Condition和线程顺序执行)