IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 线程顺序执行的8种方法,最后一种你用过吗? -> 正文阅读

[Java知识库]线程顺序执行的8种方法,最后一种你用过吗?

假如有3个线程(A,B,C),怎么让它们按照指定的顺序执行任务呢?假设希望按照 A->B->C 的顺序执行。

本文将依次介绍8种方式

目录

1.Thread.join()

2.SingleThreadExecutor(单线程池)

3.object.wait/notify(等待通知)

4.ReentrantLock-Condition(重入锁)

5.CountDownLatch(减数器)/ CyclicBarrier(栅栏)

6.Semaphore(信号量)

7.FutureTask

8.猜一猜?


下面的代码可以直接复制,运行。

1.Thread.join()

这种方式不推荐,纯粹的是为了实现这功能。

@SneakyThrowspublic static void main(String[] args) {    // 创建三个线程 A,B,C    Thread threadA = new Thread(() -> System.out.println("A"), "A");    Thread threadB = new Thread(() -> System.out.println("B"), "B");    Thread threadC = new Thread(() -> System.out.println("C"), "C");?    // 使用 Thread.join() 等待线程执行完毕, 这种方式不优雅    threadA.start();    threadA.join();?    threadB.start();    threadB.join();?    threadC.start();    threadC.join();    System.exit(0);}

执行结果:

ABC

2.SingleThreadExecutor(单线程池)

用单个线程池来实现顺序执行,这种方式比较简单,就是利用单线程池的FIFO原理。

先定义一个Runnable

@Slf4j
public?class?MyRunnable?implements?Runnable?{

????/**
?????* 自定义线程名
?????*/
????private?String threadName;

????/**
?????* CountDownLatch指令
?????*/
????private?CountDownLatch latch;

????public?MyRunnable(String threadName, CountDownLatch latch)?{
????????this.threadName = threadName;
????????this.latch = latch;
????}

????@Override
????public?void?run()
? ??{
????????try?{
????????????// 等待 countDownLatch.countDown() 命名
????????????latch.await();
????????????System.out.println(threadName);
????????}?catch?(InterruptedException e) {
????????????e.printStackTrace();
????????}
????}
}

创建单个线程池

/**
?* 利用单线程池达到顺序执行
?* {@link?java.util.concurrent.LinkedBlockingQueue} 保证了FIFO顺序
?*/
public?static?void?main(String[] args)?{
????ExecutorService executorService = Executors.newFixedThreadPool(1);
????// CountDownLatch减数机制,countDown触发await,达到同时执行的目的
????CountDownLatch latch =?new?CountDownLatch(1);

????List<String> threadNameList = Arrays.asList("A",?"B",?"C");
????for?(int?i =?0; i <?3; i++) {
????????executorService.execute(new?MyRunnable(threadNameList.get(i), latch));
????}

????latch.countDown();
????executorService.shutdown();
????while?(!executorService.isTerminated()) {
????????// 等待所有线程执行完成
????}
????System.exit(0);
}

执行结果:A->B->C

3.object.wait/notify(等待通知)

不推荐这种方式,原因:不灵活

注意这里有坑,wait()的线程必须要先执行,否则其他线程notify()是无法唤醒的。换句话说,object的这种方式,锁的使用方式一定是先wait()再notify()。

为了简单说明,这里只写A,B两个线程,以A->B顺序执行的示例。

执行代码:

@Slf4j
public?class?Test?{

????/**
?????* 使用 object.wait()/object.notify()
?????*/
????@SneakyThrows
????public?static?void?main(String[] args)?{
????????final?Object pv =?new?Object();

????????ThreadB b =?new?ThreadB();
????????ThreadA a =?new?ThreadA(b);

????????// 期望顺序 A->B
????????ExecutorService executorService = Executors.newFixedThreadPool(10);

????????// 这里必须要让B先执行才能正常执行,否则会让B一直处于wait()
????????// 原因: 如果A先拿到对象锁,执行notify()无法唤醒B, 因为B还没有拿到对象锁,还没有执行wait()
????????// 因此: 同一个锁的执行顺序一定是 wait()-notify()
????????executorService.execute(b);
????????Thread.sleep(100);
????????executorService.execute(a);


????????// 停止接受新任务,当已有任务将执行完,关闭线程池
????????executorService.shutdown();
????????while?(!executorService.isTerminated()) {
????????????// 等待所有线程执行完成
????????}
????????System.exit(0);
????}

????static?class?ThreadB?implements?Runnable?{
????????@Override
????????public?void?run()?{
????????????synchronized?(this) {
????????????????try?{
????????????????????log.info("B线程等待A线程 doing");
????????????????????// wait()的执行前提是当前线程获取了对象控制权,否则会报错:java.lang.IllegalMonitorStateException
????????????????????this.wait();
????????????????????log.info("B线程等待A线程 done");
????????????????}?catch?(InterruptedException e) {
????????????????????e.printStackTrace();
????????????????}
????????????????log.info("B线程执行完成.");
????????????}
????????}
????}

????@AllArgsConstructor
????static?class?ThreadA?implements?Runnable?{
????????private?final?Object obj;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????synchronized?(obj) {
????????????????// notify()不会立马释放对象锁,释放情景: 1.synchronized代码块执行完成; 2.主动释放 wait();
????????????????obj.notify();
????????????????log.info("A线程开始执行.");
????????????????log.info("A线程执行完成.");
????????????}
????????}
????}
}

?执行结果:

 B线程等待A线程 doing A线程开始执行. A线程执行完成. B线程等待A线程 done B线程执行完成.

4.ReentrantLock-Condition(重入锁)

这种方式比前一种方式灵活些。原理类似。

实现代码:

@Slf4j
public?class?Test?{

????/**
?????* 重入锁实现(ReentrantLock-Condition)
?????*/
????@SneakyThrows
????public?static?void?main(String[] args)?{

????????ExecutorService executorService = Executors.newFixedThreadPool(3);

????????final?SequenceLock sequenceLock =?new?SequenceLock();
????????Runnable a = () -> sequenceLock.a();
????????Runnable b = () -> sequenceLock.b();
????????Runnable c = () -> sequenceLock.c();
????????executorService.execute(a);
????????executorService.execute(b);
????????executorService.execute(c);

????????Thread.sleep(1000);
????????sequenceLock.getLock().lock();
????????try?{
????????????// 唤醒A线程,依次执行A,B,C
????????????sequenceLock.getConditionA().signal();
????????}?catch?(Exception ex) {
????????????//TODO
????????}?finally?{
????????????sequenceLock.getLock().unlock();
????????}
????????System.exit(0);
????}

????@Data
????static?class?SequenceLock?{
????????private?ReentrantLock lock =?new?ReentrantLock();
????????private?Condition conditionA = lock.newCondition();
????????private?Condition conditionB = lock.newCondition();
????????private?Condition conditionC = lock.newCondition();

????????public?void?a()?{
????????????lock.lock();
????????????try?{
????????????????log.info("A,wait signal");
????????????????// wait()的执行前提是当前线程获取了对象控制权,否则会报错:java.lang.IllegalMonitorStateException
????????????????conditionA.await();
????????????????log.info("A");
????????????????// 同 notify()一样,并不会立马释放锁,等程序全部执行完,直到 unlock
????????????????conditionB.signal();
????????????}?catch?(Exception e) {
????????????????e.printStackTrace();
????????????}?finally?{
????????????????lock.unlock();
????????????}
????????}

????????public?void?b()?{
????????????lock.lock();
????????????try?{
????????????????log.info("B,wait signal");
????????????????conditionB.await();
????????????????log.info("B");
????????????????conditionC.signal();
????????????}?catch?(Exception e) {
????????????????e.printStackTrace();
????????????}?finally?{
????????????????lock.unlock();
????????????}
????????}

????????public?void?c()?{
????????lock.lock();
????????????try?{
????????????????log.info("C,wait signal");
????????????????conditionC.await();
????????????????log.info("C");
????????????}?catch?(Exception e) {
????????????????e.printStackTrace();
????????????}?finally?{
????????????????lock.unlock();
????????????}
????????}
????}
}

执行结果:

A,wait signalB,wait signalC,wait signalABC

5.CountDownLatch(减数器)/ CyclicBarrier(栅栏)

利用CountDownLatch、CyclicBarrier 当成信号变量,作顺序开关。这里只写CountDownLatch代码,CyclicBarrier是类似的。注意:这里需要两个信号通知,因此需要定义两个CountDownLatch。

代码如下:

@Slf4j
public?class?Test?{

????/**
?????* CountDownLatch(减数器)实现/ CyclicBarrier(栅栏)实现
?????* {@link?CountDownLatch}
?????* {@link?CyclicBarrier}
?????*/
????public?static?void?main(String[] args)?{
????????// 3个线程需要2两个信号通知,A->B需要一个,B->C需要一个
????????CountDownLatch signalAB =?new?CountDownLatch(1);
????????CountDownLatch signalBC =?new?CountDownLatch(1);

????????A a =?new?A(signalAB);
????????B b =?new?B(signalAB,signalBC);
????????C c =?new?C(signalBC);

????????// 线程池中 按顺序执行 A->B->C
????????ExecutorService executorService = Executors.newFixedThreadPool(10);
????????executorService.execute(b);
????????executorService.execute(c);
????????executorService.execute(a);

????????// 停止接受新任务,当已有任务将执行完,关闭线程池
????????executorService.shutdown();
????????while?(!executorService.isTerminated()) {
????????????// 等待所有线程执行完成
????????}
????????System.out.println("over");
????????System.exit(0);

????}

????@AllArgsConstructor
????static?class?A?implements?Runnable?{
????????private?CountDownLatch latchAB;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????log.info("A");
????????????latchAB.countDown();
????????}
????}
????@AllArgsConstructor
????static?class?B?implements?Runnable?{
????????private?CountDownLatch latchAB;
????????private?CountDownLatch latchBC;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????latchAB.await();
????????????log.info("B");
????????????latchBC.countDown();
????????}
????}

????@AllArgsConstructor
????static?class?C?implements?Runnable?{
????????private?CountDownLatch latchBC;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????latchBC.await();
????????????log.info("C");
????????}
????}
}

执行结果:

A->B->C

6.Semaphore(信号量)

其实跟CountDownLatch 一样,都是作为信号量来传递。同样定义两个信号量。执行结果仍然是A->B->C。

代码如下:

@Slf4j
public?class?Test?{

????/**
?????* Semaphore 信号量实现
?????* {@link?Semaphore}
?????*/
????public?static?void?main(String[] args)?{
????????// 3个线程需要2两个信号量,A->B需要一个,B->C需要一个
????????Semaphore signalAB =?new?Semaphore(0);
????????Semaphore signalBC =?new?Semaphore(0);

????????A a =?new?A(signalAB);
????????B b =?new?B(signalAB, signalBC);
????????C c =?new?C(signalBC);

????????// 线程池中 按顺序执行 A->B->C
????????ExecutorService executorService = Executors.newFixedThreadPool(10);
????????executorService.execute(b);
????????executorService.execute(c);
????????executorService.execute(a);

????????// 停止接受新任务,当已有任务将执行完,关闭线程池
????????executorService.shutdown();
????????while?(!executorService.isTerminated()) {
????????????// 等待所有线程执行完成
????????}
????????System.out.println("over");
????????System.exit(0);

????}

????@AllArgsConstructor
????static?class?A?implements?Runnable?{
????????private?Semaphore semaphoreAB;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????log.info("A");
????????????semaphoreAB.release();
????????}
????}

????@AllArgsConstructor
????static?class?B?implements?Runnable?{
????????private?Semaphore semaphoreAB;
????????private?Semaphore semaphoreBC;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????// semaphore 信号量-1,总数为0的时候会等待
????????????semaphoreAB.acquire();
????????????log.info("B");
????????????// semaphore 信号量+1
????????????semaphoreBC.release();
????????}
????}

????@AllArgsConstructor
????static?class?C?implements?Runnable?{
????????private?Semaphore semaphoreBC;

????????@SneakyThrows
????????@Override
????????public?void?run()?{
????????????semaphoreBC.acquire();
????????????log.info("C");
????????}
????}
}

7.FutureTask

通过FutureTask的阻塞特性直接实现线程的顺序执行,这种方式比较简单,有点类似于单线程池执行。执行结果仍然是A->B->C。

代码如下:

@Slf4j
public?class?ThreadABC?{
????/**
?????* FutureTask 阻塞特性实现
?????*/
????public?static?void?main(String[] args)?throws?ExecutionException, InterruptedException?{
????????ExecutorService executorService = Executors.newFixedThreadPool(3);

????????// 实际上 ExecutorService.submit 还是用的 FutureTask
????????Object a = executorService.submit(new?Runnable() {
????????????@SneakyThrows
????????????@Override
????????????public?void?run()?{
????????????????log.info("A");
????????????}
????????}).get();

????????Object b = executorService.submit(new?Runnable() {
????????????@SneakyThrows
????????????@Override
????????????public?void?run()?{
????????????????log.info("B");
????????????}
????????}).get();

????????Object c = executorService.submit(new?Runnable() {
????????????@SneakyThrows
????????????@Override
????????????public?void?run()?{
????????????????log.info("C");
????????????}
????????}).get();

????????log.info("main thread done.");

????????System.exit(0);
????}
}

8.CompletableFuture (推荐)

JDK1.8中 CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程。提供函数式编程的能力,可帮助我们完成复杂的线程的阶段行编程(CompletionStage)。具体这里不详细介绍,不了解的朋友可以去网上查一下资料。

执行代码:

@Slf4j
public?class?ThreadABC?{

????/**
?????* CompletableFuture (推荐)
?????* JDK1.8中 CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程,
?????* 提供函数式编程的能力,可帮助我们完成复杂的线程的阶段行编程(CompletionStage)
?????* {@link?java.util.concurrent.CompletableFuture}
?????*/
????public?static?void?main(String[] args)?throws?InterruptedException?{
????????ExecutorService executorService = Executors.newFixedThreadPool(3);

????????// 有a,b,c三个线程(任务)
????????Runnable a = () -> log.info("A");
????????Runnable b = () -> log.info("B");
????????Runnable c = () -> log.info("C");

????????// 异步执行
????????CompletableFuture.runAsync(a, executorService).thenRun(b).thenRun(c);

????????log.info("main thread.");
????????// 停止接受新任务,当已有任务将执行完,关闭线程池
????????executorService.shutdown();
????????while?(!executorService.isTerminated()) {
????????????// 等待所有线程执行完成
????????}
????????System.exit(0);
????}
}

执行结果:

main thread.ABC

多线程的执行顺序,在面试过程中经常会被问到。不了解的,可以收藏一下。笔者建议不了解CompletableFuture,可以去尝试写一些例子。功能真的很强大!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-21 15:13:47  更:2021-08-21 15:14:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:11:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码