美文网首页
线程任务按顺序执行的N种实现

线程任务按顺序执行的N种实现

作者: flycash | 来源:发表于2019-02-17 13:56 被阅读7次

问题

最近遇到了一个小问题,就是不同的线程执行不同的任务,但是这些任务是有依赖关系的。这个问题抽象出来就是:
假设有三个线程,分别打印a, b, c。我们要求最终输出的结果是:
abc,abc,abc,abc打印十次,而后输出end。
也就是最终的输出会是
abc,abc,abc,abc,abc,abc,abc,abc,abc,abc,end

注意的是,三个线程分别打印abc,也就是说主线程要打印逗号和end。

第一种解法: Join

这应该算是最正统的解决方案。Thread的join()方法可以使线程停下来等待另外一个线程执行完毕。
代码是:

    private void solution1() throws Exception{
        for (int i = 0; i < 10; i ++) {
            printABC();
        }
        System.out.print("end");
    }

    private void printABC() throws Exception{
        Thread a = new Thread(()-> {
            System.out.print("a");
        });

        Thread b = new Thread(() -> {
            try {
                a.join();
                System.out.print("b");
            } catch (Exception e) {

            }

        });

        Thread c = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    b.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                System.out.print("c");
            }
        });

        a.start();
        b.start();
        c.start();
        c.join();
        System.out.print(",");
    }

使用Semphore

我们的需求里面,第一个是要在输出a之后输出b,我们就可以创建一个信号量,使得输出a之后加1,而在输出b之后就减一。其余部分也是如此处理。

    private Semaphore aSem = new Semaphore(1);
    private Semaphore bSem = new Semaphore(1);
    private Semaphore cSem = new Semaphore(1);

    private void solution2() throws Exception {
        aSem.acquire();
        bSem.acquire();
        cSem.acquire();

        for (int i=0; i < 10; i++) {


            this.executor.execute(() -> {
                System.out.print("a");
                aSem.release();
            });

            this.executor.execute(()-> {
                try {
                    aSem.acquire();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.print("b");
                bSem.release();
            });


            this.executor.execute( () -> {
                try {
                    bSem.acquire();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.print("c");
                cSem.release();
            });

            cSem.acquire();
            System.out.print(",");

        }

        System.out.print("end");
    }
}

这种做法其实很猥琐的。毕竟Semaphore并不是用于这种场景。

第二种解法:使用notify和wait

    private final Object aMonitor = new Object();
    private final Object bMonitor = new Object();
    private final Object cMonitor = new Object();

    private void solution2() throws Exception {

        for (int i=0; i < 10; i++) {

            this.executor.execute( () -> {
                try {
                    synchronized (bMonitor) {
                        bMonitor.wait();
                        System.out.print("c");
                    }
//                    bMonitor.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                synchronized (cMonitor) {
                    cMonitor.notifyAll();
                }
            });

            this.executor.execute(()-> {

                try {

                    synchronized (aMonitor) {
                        aMonitor.wait();
                        System.out.print("b");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (bMonitor) {
                    bMonitor.notifyAll();
                }
            });

            this.executor.execute(() -> {
                synchronized (aMonitor) {
                    System.out.print("a");
                    aMonitor.notifyAll();
                }
            });

            synchronized (cMonitor) {
                cMonitor.wait();
            }
            System.out.print(",");

        }

        System.out.print("end");
    }

第三种解法:使用CountDownLatch

CountDownLatch可以用于任务间的等待。比如说任务A需要等任务BCD执行完才能往下执行,就可以用CountDownLatch(3)来实现。

    private void solution2() throws Exception {

        for (int i=0; i < 10; i++) {
            CountDownLatch aLatch = new CountDownLatch(1);
            CountDownLatch bLatch = new CountDownLatch(1);
            CountDownLatch cLatch = new CountDownLatch(1);
            this.executor.execute( () -> {
               System.out.print("a");
               aLatch.countDown();
            });

            this.executor.execute(()-> {

                try {
                    aLatch.await();
                    System.out.print("b");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                bLatch.countDown();
            });

            this.executor.execute(() -> {
                try {
                    bLatch.await();
                    System.out.print("c");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                cLatch.countDown();
            });

            cLatch.await();
            System.out.print(",");

        }

        System.out.print("end");
    }

第四种解法:使用ReentrantLock和Condition

    private ReentrantLock lock = new ReentrantLock();
    private Condition aEnd = lock.newCondition();
    private Condition bEnd = lock.newCondition();
    private Condition cEnd = lock.newCondition();

    private void solution2() throws Exception {

        for (int i=0; i < 10; i++) {



            this.executor.execute(() -> {
                try {
                    lock.lock();
                    bEnd.await();
                    System.out.print("c");
                    cEnd.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            });

            this.executor.execute(()-> {

                try {
                    lock.lock();
                    aEnd.await();
                    System.out.print("b");
                    bEnd.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            });

            this.executor.execute( () -> {
                try {
                    lock.lock();
                    System.out.print("a");
                    aEnd.signal();
                } finally {
                    lock.unlock();
                }

            });

            try {
                lock.lock();
                cEnd.await();
                System.out.print(",");
            } finally {
                lock.unlock();
            }
        }

        System.out.print("end");
    }

第五种解法:使用阻塞队列

在这种解法里面,核心思想就在于将问题看做是一个生产者-消费者模型。相当于线程1生产了a之后,线程2取出a,同时生产出来一个b。以此递归。


    private ArrayBlockingQueue<Object> aQueue = new ArrayBlockingQueue<>(1);
    private ArrayBlockingQueue<Object> bQueue = new ArrayBlockingQueue<>(1);
    private ArrayBlockingQueue<Object> cQueue = new ArrayBlockingQueue<>(1);

    private void solution2() throws Exception {
        Object obj = new Object();
        for (int i=0; i < 10; i++) {



            this.executor.execute(() -> {
                try {
                    System.out.print("a");
                    aQueue.put(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

            this.executor.execute(()-> {
                try {
                    aQueue.take();
                    System.out.print("b");
                    bQueue.put(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

            this.executor.execute( () -> {
                try {
                    bQueue.take();
                    System.out.print("c");
                    cQueue.put(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }


            });

            cQueue.take();
            System.out.print(",");
        }

        System.out.print("end");
    }

相关文章

  • 线程任务按顺序执行的N种实现

    问题 最近遇到了一个小问题,就是不同的线程执行不同的任务,但是这些任务是有依赖关系的。这个问题抽象出来就是:假设有...

  • 【基础】练习册51-Python3_单线程:按顺序执行任务

    单线程:按顺序执行任务 代码如下: #coding=utf-8#单线程:按顺序执行任务from time impo...

  • iOS 多线程-CGD

    串行队列同步执行,不开启新线程,任务按顺序执行 串行队列异步执行,会开启新线程(1个),任务按照顺序执行 并行队列...

  • Android线程池使用

    一:无大小限制的线程池执行效果如下 二:限制按顺序来执行任务的线程池效果如下 四:按指定个数来执行任务的线程池效果...

  • iOS 多线程

    线程概念 同步: 任务按顺序执行, 有先后顺序, 执行完一个才能执行另一个任务, 任务有一个执行者 异步 有多个执...

  • iOS中使用GCD对线程和任务执行顺序的测试

    GCD多线程测试 小结: 同步执行任务1. 一定不会开启线程,任务是按顺序串行执行2. 如果在主队列执行任务,会引...

  • C#沉淀-异步编程 一

    什么是异步 任务以固定的顺序被执行叫做同步,任务不按固定顺序执行则叫做异步 关于进程与线程 启动程序时,系统会在内...

  • 多线程详解-NSThread、GCD、NSOperation

    线程的串行 一个线程中任务的执行是串行的如果要再一个线程中执行多个任务,那么只能一个一个的按顺序执行这些任务也就是...

  • iOS重识

    1synchronized(互斥锁) 互斥锁,就是使用了线程同步技术,多条线程按顺序地执行任务 使用场景:多条线程...

  • 同步任务与异步任务执行顺序

    JavaScript是单线程执行的,即 js 中任务是按顺序依次执行的,但若其中一个任务执行时间过长,后续任务会一...

网友评论

      本文标题:线程任务按顺序执行的N种实现

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