zoukankan      html  css  js  c++  java
  • java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)

    多线程应用中,经常会遇到这种场景:后面的处理,依赖前面的N个线程的处理结果,必须等前面的线程执行完毕后,后面的代码才允许执行。

    在我不知道CyclicBarrier之前,最容易想到的就是放置一个公用的static变量,假如有10个线程,每个线程处理完上去累加下结果,然后后面用一个死循环(或类似线程阻塞的方法),去数这个结果,达到10个,说明大家都爽完了,可以进行后续的事情了,这个想法虽然土鳖,但是基本上跟语言无关,几乎所有主流编程语言都支持。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    package yjmyzz.test;
     
     
    public class ThreadLockTest {
     
        public static int flag = 0;//公用变量
     
        public static void main(String[] args) throws Exception {
            ThreadLockTest testObj = new ThreadLockTest();
            final int threadNum = 10;
     
            for (int i = 0; i < threadNum; i++) {
                new Thread(new MyRunable(i, testObj)).start();
            }
     
            while (true) {
                if (testObj.flag >= threadNum) {
                    System.out.println("----------- 所有thread执行完成!");
                    break;
                }
                Thread.sleep(10);
            }
        }
     
        static class MyRunable implements Runnable {
            int _i = 0;
            ThreadLockTest _test;
     
            public MyRunable(int i, ThreadLockTest test) {
                this._i = i;
                this._test = test;
            }
     
            @Override
            public void run() {
                try {
                    Thread.sleep((long) (Math.random() * 10));
                    System.out.println("thread " + _i + " done");
                    //利用synchronized获得同步锁
                    synchronized (_test) {
                        _test.flag += 1;
                    }
                    System.out.println("thread " + _i + " => " + _test.flag);//测试用
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    输出结果:

    thread 0 done
    thread 0 => 1
    thread 9 done
    thread 9 => 2
    thread 1 done
    thread 1 => 3
    thread 3 done
    thread 3 => 4
    thread 7 done
    thread 7 => 5
    thread 6 done
    thread 6 => 6
    thread 2 done
    thread 2 => 7
    thread 4 done
    thread 4 => 8
    thread 8 done
    thread 8 => 9
    thread 5 done
    thread 5 => 10
    -----------
    所有thread执行完成!

    除了这个方法,还可以借助FutureTask,达到类似的效果,其get方法会阻塞线程,等到该异步处理完成。缺点就是,FutureTask调用的是Callable,必须要有返回值,所以就算你不想要返回值,也得返回点啥

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package yjmyzz.test;
     
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
     
    public class FutureTaskTest {
     
        public static void main(String[] args) throws ExecutionException, InterruptedException {
     
            FutureTask<String>[] tasks = new FutureTask[10];
     
            for (int i = 0; i < tasks.length; i++) {
                final int j = i;
                tasks[i] = new FutureTask<String>(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        Thread.sleep((long) (Math.random() * 100));
                        return "task" + j + " done";
                    }
                });
                new Thread(tasks[i]).start();
            }
     
            for (int i = 0; i < tasks.length; i++) {
                System.out.println(tasks[i].get());//依次等待所有task执行完毕
            }
     
            System.out.println("----------- 所有task执行完成!");
     
        }
    }

    执行结果:

    task0 done
    task1 done
    task2 done
    task3 done
    task4 done
    task5 done
    task6 done
    task7 done
    task8 done
    task9 done
    -----------
    所有task执行完成!

      

    此外,Thread的Join方法也可以实现类似的效果,主要代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public static void main(String[] args) throws Exception {
     
        final int threadNum = 10;
        Thread[] threads = new Thread[threadNum];
     
        for (int i = 0; i < threadNum; i++) {
            threads[i] = new Thread(new MyRunable(i));
            threads[i].start();
        }
     
        for (int i = 0; i < threadNum; i++) {
            threads[i].join();
        }
     
        System.out.println("----------- 所有thread执行完成!");
     
    }

    当然,这个需求最“正统”的解法应该是使用CyclicBarrier,它可以设置一个所谓的“屏障点”(或称集合点),好比在一项团队活动中,每个人都是一个线程,但是规定某一项任务开始前,所有人必须先到达集合点,集合完成后,才能继续后面的任务。  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package yjmyzz.test;
     
    import java.util.concurrent.CyclicBarrier;
     
    public class ThreadTest {
     
        public static void main(String[] args) throws Exception {
     
            final int threadNum = 10;
            CyclicBarrier cb = new CyclicBarrier(threadNum + 1);//注意:10个子线程 + 1个主线程
     
            for (int i = 0; i < threadNum; i++) {
                new Thread(new MyRunable(cb, i)).start();
            }
     
            cb.await();
            System.out.println("----------- 所有thread执行完成!");
        }
     
        static class MyRunable implements Runnable {
            CyclicBarrier _cb;
            int _i = 0;
     
            public MyRunable(CyclicBarrier cb, int i) {
                this._cb = cb;
                this._i = i;
            }
     
            @Override
            public void run() {
                try {
                    Thread.sleep((long) (Math.random() * 100));
                    System.out.println("thread " + _i + " done,正在等候其它线程完成...");
                    _cb.await();
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    thread 9 done,正在等候其它线程完成...
    thread 5 done,正在等候其它线程完成...
    thread 0 done,正在等候其它线程完成...
    thread 6 done,正在等候其它线程完成...
    thread 4 done,正在等候其它线程完成...
    thread 2 done,正在等候其它线程完成...
    thread 3 done,正在等候其它线程完成...
    thread 8 done,正在等候其它线程完成...
    thread 7 done,正在等候其它线程完成...
    thread 1 done,正在等候其它线程完成...
    -----------
    所有thread执行完成!

    参考文章:

    http://ifeve.com/concurrency-cyclicbarrier/

    http://ifeve.com/thread-synchronization-utilities-5/

    http://ifeve.com/semaphore-countdownlatch-cyclicbarrier-phaser-exchanger-in-java/  

    http://ifeve.com/thread-management-7/

  • 相关阅读:
    webpack优化
    原生js实现平滑滚动
    【大前端攻城狮之路】JavaScript函数式编程
    【大前端攻城狮之路】面试集锦
    【大前端攻城狮之路·二】Javascript&QA⼯程师
    【vue系列之三】从一个vue-pdf-shower,说说vue组件和npm包
    display:box和display:flex填坑之路
    【vue系列之一】使用vue-cli脚手架工具搭建vue-webpack项目
    快速上手微信小程序-快递100
    js面向对象小结(工厂模式,构造函数,原型方法,继承)
  • 原文地址:https://www.cnblogs.com/tuojunjie/p/7390700.html
Copyright © 2011-2022 走看看