美文网首页
多个线程等待

多个线程等待

作者: 麦兜的夏天 | 来源:发表于2019-05-31 22:51 被阅读0次

    场景一:

    假设在一个线程 A 中开启了另一个线程 B,A 线程依赖 B 线程的计算结果,可是直接开启线程后,很有可能 A 线程比 B 线程跑得快,也就是说还没等 B 线程计算出结果,A 线程已经结束了。

    • 存在问题的代码:
    public static void main(String[] args) throws Exception {
            Thread parser1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();  //temp 对象的 a 属性初始化值为 0
                    temp.a += 100;
                    System.out.println("parser1 finish");
                }
            });
            parser1.start();
            
            temp temp = new temp();
            System.out.println(temp.a);       
    } 
    
    //main 函数属于一个线程,它内部开启了 parser1 这个线程。
    打印出的结果是 0 ,而不是 100 。
    

    怎么解决这个问题?
    让线程 A 等待着线程 B,直到线程 B 结束之后再继续进行。 只需要在线程 B 开启之后,再调用 join() 方法阻塞线程,就可以了。

    • 改善代码
    public static void main(String[] args) throws Exception {
            Thread parser1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a += 100;
                    System.out.println("parser1 finish");
                }
            });
            parser1.start();
            parser1.join();  //阻塞线程
            
            temp temp = new temp();
            System.out.println(temp.a);       
    } 
    
    打印的结果为 100
    

    场景二:

    假如在线程 A 里面有 B、C 这几个线程,A 依赖于 B 和 C 线程的执行结果,并且 B 和 C 之间也有某种联系,只有当 B 和 C 在某个固定的点上同步执行,它们才能默契的完成任务。


    两个线程之间的关系.png
        public static void main(String[] args) throws Exception {
            //temp 对象的静态变量 a 初始值等于 0
            //如果第一个线程先执行,那么 a 就等于 1
            //这时候再执行第二个线程,a 会变成 100;
            //如果它们的执行顺序正好相反,那么 a 就变成 1 了。
            Thread parser1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a += 1;
    
                    try {
                        System.out.println("线程一其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程一执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread parser2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a *= 100;
    
                    try {
                        System.out.println("线程二其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程二执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
         
        }
    
    • 粗糙的方案:
      如果只是简单粗暴的调用 join() 方法让线程阻塞,然后调整他们的执行顺序,其实是一种低效的方法。main 想要拿到最后的计算结果,必须等到两个线程都走完之后。
        public static void main(String[] args) throws Exception {
            Thread parser1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a += 1;
    
                    try {
                        System.out.println("线程一其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程一执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread parser2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a *= 100;
    
                    try {
                        System.out.println("线程二其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程二执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            //parser1 和 parser2 之间是平等的竞争关系,哪个先执行不一定,所以让第二个线程等待1秒
            parser1.start();
            parser2.start();
            parser1.join();
            parser2.join(1000L);
            System.out.println(new temp().a);       
        }
        
    //执行结果
    线程一其余代码被执行
    线程二其余代码被执行
    线程一执行结束  
    线程二执行结束
    (。。。。经历漫长的等待之后。。。。)
    100   
    
    • 优化方案:
      我们可以使用并发包中的 CountDownLatch 来提高效率
        static CountDownLatch c = new CountDownLatch(2);   
        //构造函数中传入参数作为计数器,如果想要等待N个点完成,就传入N
    
        public static void main(String[] args) throws Exception {
            Thread parser1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    temp.a += 1;
                    c.countDown();    //计数器减一
    
                    try {
                        System.out.println("线程一其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程一执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread parser2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    temp temp = new temp();
                    c.countDown();   //计数器减二
                    temp.a *= 100;
    
                    try {
                        System.out.println("线程二其余代码被执行");
                        Thread.sleep(5000L);
                        System.out.println("线程二执行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            });
    
            parser1.start();
            parser2.start();
    
            c.await();   //计数器等于0时,不再阻塞当前线程
    
            System.out.println(new temp().a);
        }
        
    //执行结果
    线程二其余代码被执行
    线程一其余代码被执行
    100
    (。。。。经历漫长的等待之后。。。。)
    线程二执行结束
    线程一执行结束  
    

    相关文章

      网友评论

          本文标题:多个线程等待

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