美文网首页
多线程(12) — Condition线程同步通信

多线程(12) — Condition线程同步通信

作者: 烧杰 | 来源:发表于2018-04-17 22:25 被阅读0次

    Condition的功能类似于Object.wait()和Object.notify()功能,来实现线程间的协调与通信。

    之前的步互斥与通信(练习)例子用condition实现:

    package ThreadPractice;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by qiaorenjie on 2018/3/30.
     */
    public class ConditionCommunication {
        /*
           问:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,
           接着再回到主线程又循环100次,如此循环50次。
            */
        public static void main(String[] args) {
    
            final Business business = new Business();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 50; i++) {
                        business.sub(i);    // 这个方法结束business会释放,所以要加final,而主线程则可不加
                    }
    
                }
            }).start();
    
            // main 方法占用的主线程直接用main的线程
            for (int i = 1; i <= 50 ; i++) {
                business.main(i);
            }
    
        }
    
        /*
            放在本类ConditionCommunication外就相当于Business.java与外面的重复了会报错,
            而拉到类的内部就是内部内就是ConditionCommunication.Buiness.java,静态方法调用时候再加静态
        */
         static class Business {
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();  // condition基于lock上
            private boolean flag = true;
    
            public void sub(int i) {
                lock.lock();
                try {
                    while (!flag){
                        try {
                            condition.await();  // 自己的 await方法不要写错obj的 wait
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for (int j = 1; j <= 10; j++) {
                        System.out.println("子线程循环:" + j + "  循环了:" + i);
                    }
                    flag = !flag;
                    condition.signal();        // signal代替notify
                }finally {
                    lock.unlock();
                }
    
            }
    
            public synchronized void main(int i) {
                lock.lock();
                try {
                    while (flag){
                        try {
                            condition.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for (int j = 1; j <= 100; j++) {
                        System.out.println("主线程循环:" + j + " 循环了:" + i);
                    }
                    flag = !flag;
                    condition.signal();
                }finally {
                    lock.unlock();
                }
            }
    
        }
    
    
    }
    
    

    Condition与wait与notify想比能实现其不能实现的功能,比如:阻塞队列、三个或多个线程唤醒指定线程等

    Obj下的notify()用于唤醒其中一个线程,而notifyAll()则是唤醒全部线程,所以想完成唤醒指定线程不太方便,而利用condition则非常容易。
    condition可以理解成一个线程间协调通信的工具类,让线程在某个条件下(condition)等待await(),让对应线程的条件被传递信号signal时说明线程又可以重新唤醒恢复。

    比如在之前做的子线程与主线程的练习下又有新的需求:
    (新增需求) : 在1号线程完成循环后唤醒2号,2号完成后唤醒3号,3号再到1号循环。

    public class ThreeConditionCommunication {
        /*
           问:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次。在1号线程完成循环后唤醒2号,2号完成后唤醒3号,3号再到1号循环。
            */
        public static void main(String[] args) {
    
            final Business business = new Business();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 50; i++) {
                        business.sub2(i);    // 这个方法结束business会释放,所以要加fina,而主线程则可不加
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 50; i++) {
                        business.sub3(i);    // 这个方法结束business会释放,所以要加fina,而主线程则可不加
                    }
                }
            }).start();
    
            // main 方法占用的主线程直接用main的线程
            for (int i = 1; i <= 50 ; i++) {
                business.main(i);
            }
        }
    
        /*
            放在本类ConditionCommunication外就相当于Business.java与外面的重复了会报错,
            而拉到类的内部就是内部内就是ConditionCommunication.Buiness.java,静态方法调用时候再加静态
        */
         static class Business {
            Lock lock = new ReentrantLock();
            Condition condition1 = lock.newCondition();
            Condition condition2 = lock.newCondition();
            Condition condition3 = lock.newCondition();
            private int turn = 1;
    
            public void sub2(int i) {
                lock.lock();
                try {
                    while (turn != 2){      // !=2说明不该线程2
                        try {
                            condition2.await();     //不该2那么2就等着
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for (int j = 1; j <= 10; j++) {
                        System.out.println("线程2号循环:" + j + "  循环了:" + i);
                    }
                    turn = 3;
                    condition3.signal();        // 交给第三个线程
                }finally {
                    lock.unlock();
                }
    
            }
    
            public void sub3(int i) {
                lock.lock();
                try {
                    while (turn != 3){
                        try {
                            condition3.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for (int j = 1; j <= 20; j++) {
                        System.out.println("线程3号循环:" + j + "  循环了:" + i);
                    }
                    turn = 1;
                    condition1.signal();
                }finally {
                    lock.unlock();
                }
    
            }
    
            public synchronized void main(int i) {
                lock.lock();
                try {
                    while (turn != 1){
                        try {
                            condition1.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for (int j = 1; j <= 100; j++) {
                        System.out.println("线程1号循环:" + j + " 循环了:" + i);
                    }
                    turn = 2;
                    condition2.signal();
                }finally {
                    lock.unlock();
                }
            }
        }
    }
    
    

    通过对不同线程对应的condition进行signal达到唤醒指定线程的操作。

    那么Condition是怎么实现的呢?

    image.png

    由代码点击进入发现lock.newCondition() 返回的是Lock接口下的Condition接口的实现,查询Condition发现该实现被一个叫 AbstractQueuedSynchronizer (AQS) 同步队列中被实现,如下:


    image.png

    类ConditionObject实现了Condition接口,而这个类作为了AbstractQueuedSynchronizer的内部类


    image.png

    这里又是利用到了AQS的原理,AQS在多线程中有着很重要的地位。

    相关文章

      网友评论

          本文标题:多线程(12) — Condition线程同步通信

          本文链接:https://www.haomeiwen.com/subject/lfobcftx.html