java 多线程按照顺序执行任务

1、多线程按照顺序执行任务,方法如下: 

  • 使用线程的join方法
  • 使用主线程的join方法
  • 使用线程的线程池方法
  • 使用线程的CountDownLatch(倒计数)方法
  • 使用线程的CyclicBarrier(回环栅栏)方法
  • 使用线程的Semaphore(信号量)方法

2.多线程并发执行,等全部执行完成后在继续执行往下程序,方法如下:

  • 使用线程的CountDownLatch(倒计数)方法    ----具体看该篇文章有写,这里不具体详细说明
  • 使用线程的CyclicBarrier(回环栅栏)方法        ----具体看该篇文章有写,这里不具体详细说明


我们下面需要完成这样一个应用场景:

1.早上;2.测试人员、产品经理、开发人员陆续的来公司上班;3.产品经理规划新需求;4.开发人员开发新需求功能;5.测试人员测试新功能。

规划需求,开发需求新功能,测试新功能是一个有顺序的,我们把thread1看做产品经理,thread2看做开发人员,thread3看做测试人员。

(1)、使用线程的join方法
join():是Theard的方法,作用是调用线程需等待该join()线程执行完成后,才能继续向下运行。
应用场景当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法
 

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("产品经理规划新需求");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                thread1.join();
                System.out.println("开发人员开发新需求功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                thread2.join();
                System.out.println("测试人员测试新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("测试人员来上班了...");
    thread3.start();
    System.out.println("产品经理来上班了...");
    thread1.start();
    System.out.println("开发人员来上班了...");
    thread2.start();
}

输出如下:
早上:
测试人员来上班了...
产品经理来上班了...
开发人员来上班了...
产品经理规划新需求
开发人员开发新需求功能
测试人员测试新功能

2、使用主线程的join方法
这里是在主线程中使用join()来实现对线程的阻塞。
 

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("产品经理正在规划新需求...");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("开发人员开发新需求功能");
        }
    });

    final Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("测试人员测试新功能");
        }
    });

    try {

        System.out.println("开发人员和测试人员休息中...");
        System.out.println("早上:");
        System.out.println("产品经理来上班了");
        System.out.println("测试人员来上班了");
        System.out.println("开发人员来上班了");
        thread1.start();
        //在父进程调用子进程的join()方法后,父进程需要等待子进程运行完再继续运行。
        thread1.join();
        System.out.println("产品经理新需求规划完成!");
        thread2.start();
        System.out.println("测试人员休息会...");
        thread2.join();
        thread3.start();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

输出结果如下:
开发人员和测试人员休息中...
早上:
产品经理来上班了
测试人员来上班了
开发人员来上班了
产品经理正在规划新需求...
产品经理新需求规划完成!
测试人员休息会...
开发人员开发新需求功能
测试人员测试新功能

3、使用线程的线程池方法
JAVA通过Executors提供了四种线程池

  • 单线程化线程池(newSingleThreadExecutor);
  • 可控最大并发数线程池(newFixedThreadPool);
  • 可回收缓存线程池(newCachedThreadPool);
  • 支持定时与周期性任务的线程池(newScheduledThreadPool)。

单线程化线程池(newSingleThreadExecutor):优点,串行执行所有任务。

submit():提交任务。

shutdown():方法用来关闭线程池,拒绝新任务。

应用场景:串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行

 

static ExecutorService executorService = Executors.newSingleThreadExecutor();

public static void main(String[] args) throws Exception {

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("产品经理规划新需求");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("开发人员开发新需求功能");
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("测试人员测试新功能");
        }
    });

    System.out.println("早上:");
    System.out.println("产品经理来上班了");
    System.out.println("测试人员来上班了");
    System.out.println("开发人员来上班了");
    System.out.println("领导吩咐:");
    System.out.println("首先,产品经理规划新需求...");
    executorService.submit(thread1);
    System.out.println("然后,开发人员开发新需求功能...");
    executorService.submit(thread2);
    System.out.println("最后,测试人员测试新功能...");
    executorService.submit(thread3);
    executorService.shutdown();
}

输出如下:
早上:
产品经理来上班了
测试人员来上班了
开发人员来上班了
领导吩咐:
首先,产品经理规划新需求...
然后,开发人员开发新需求功能...
最后,测试人员测试新功能...
产品经理规划新需求
开发人员开发新需求功能
测试人员测试新功能

4、.使用线程的CountDownLatch(倒计数)方法
 

/**
 * 用于判断线程一是否执行,倒计时设置为1,执行后减1
 */
private static CountDownLatch c1 = new CountDownLatch(1);

/**
 * 用于判断线程二是否执行,倒计时设置为1,执行后减1
 */
private static CountDownLatch c2 = new CountDownLatch(1);

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("产品经理规划新需求");
            //对c1倒计时-1
            c1.countDown();
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //等待c1倒计时,计时为0则往下运行
                c1.await();
                System.out.println("开发人员开发新需求功能");
                //对c2倒计时-1
                c2.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //等待c2倒计时,计时为0则往下运行
                c2.await();
                System.out.println("测试人员测试新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("测试人员来上班了...");
    thread3.start();
    System.out.println("产品经理来上班了...");
    thread1.start();
    System.out.println("开发人员来上班了...");
    thread2.start();

输出结果如下:
早上:
测试人员来上班了...
产品经理来上班了...
开发人员来上班了...
产品经理规划新需求
开发人员开发新需求功能
测试人员测试新功能

5、使用CyclicBarrier(回环栅栏)实现线程按顺序运行

 

static CyclicBarrier barrier1 = new CyclicBarrier(2);
static CyclicBarrier barrier2 = new CyclicBarrier(2);

public static void main(String[] args) {

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("产品经理规划新需求");
                //放开栅栏1
                barrier1.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //放开栅栏1
                barrier1.await();
                System.out.println("开发人员开发新需求功能");
                //放开栅栏2
                barrier2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    final Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //放开栅栏2
                barrier2.await();
                System.out.println("测试人员测试新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("测试人员来上班了...");
    thread3.start();
    System.out.println("产品经理来上班了...");
    thread1.start();
    System.out.println("开发人员来上班了...");
    thread2.start();

输出结果:
早上:
测试人员来上班了...
产品经理来上班了...
开发人员来上班了...
产品经理规划新需求
开发人员开发新需求功能
测试人员测试新功能

6、使用Sephmore(信号量)实现线程按顺序运行
 

Sephmore(信号量):Semaphore是一个计数信号量,从概念上将,Semaphore包含一组许可证,如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证,每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。

acquire():当前线程尝试去阻塞的获取1个许可证,此过程是阻塞的,当前线程获取了1个可用的许可证,则会停止等待,继续执行。

release():当前线程释放1个可用的许可证。

 

public static Semaphore semaphoreA = new Semaphore(1);
public static Semaphore semaphoreB = new Semaphore(1);
public static Semaphore semaphoreC = new Semaphore(1);

public static void main(String[] args) throws InterruptedException {
    semaphoreB.acquire();//ABC线程启动之前 获取SemaphoreB的1个资源,保证线程A最先执行
    semaphoreC.acquire();//ABC线程启动之前 获取SemaphoreC的1个资源,保证线程A最先执行
    Thread a=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreA.acquire();
                System.out.print("A");
                semaphoreB.release();//之前说的特性:可以在ThreadA释放ThreadB的Semaphore资源, 下同
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    Thread b=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreB.acquire();
                System.out.print("B");
                semaphoreC.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    Thread c=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreC.acquire();
                System.out.println("C");
                semaphoreA.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    c.start();
    b.start();
    a.start();
}

直接结果如下:
ABC

本次使用三个信号量来控制三个线程按照顺序执行

具体参考:https://www.cnblogs.com/wenjunwei/p/10573289.html  这篇文章中的信号量程序有问题
https://www.jianshu.com/p/bed37328e3b0   使用这篇文章信号量程序问题
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章