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
本次使用三个信号量来控制三个线程按照顺序执行
具体参考: