让 3 个线程串行的几种方式

1、通过join()的方式

子线程调用join()的时候,主线程等待子线程执行完再执行。如果让多个线程顺序执行的话,那么需要他们按顺序调用start()。

java 复制代码
   /**
     * - 第一个迭代(i=0):
     *     启动线程t1 -> 然后调用t1.join()。
     *     主线程(执行testMethod2的线程)会在t1.join()处阻塞,直到t1线程执行完毕。
     * - 第二个迭代(i=1):
     *     只有等到t1执行完毕,主线程才会继续执行,然后启动线程t2,并调用t2.join(),主线程等待t2执行完毕。
     * - 第三个迭代(i=2):
     *     同样,主线程等待t2执行完毕后,启动t3,然后等待t3执行完毕。
     * @throws InterruptedException
     */
    @Test
    void testMethod1() throws InterruptedException {
        List<Thread> list = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            Thread thread = new Thread(() -> {
                log.info("{} 执行~", Thread.currentThread().getName());
            },"t" + i);
            list.add(thread);
        }
        for (int i = 0; i < list.size(); i++) {
            list.get(i).start();
            //他阻塞的是主线程,当前线程执行完后,主线程才会执行
            list.get(i).join();
        }
    }

2、通过CountDownLatch 的方式

countDown():没调用一次,计数器就会减 1。当计数器减到 0 时,调用await()的线程才会被唤醒。

await():当计数器不为 0 的时候,调用await()的线程会被阻塞。

java 复制代码
   /**
     *  1、让第一个线程执行,完事调用countDown()再对countDownLatch记数减 1
     *  2、第二个线程调用await()去唤醒,但是需要线程一执行完,
     *     因为只有记数为 0 的话,当前线程才会被唤醒,依次类推
     */
    @Test
    void testMethod2(){
        CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 1; i <= 3; i++) {
            final int threadId = i;
            new Thread(() -> {
                if (threadId != 1) {
                    try {
                        //等待计数器归零,再继续往下执行,否则在此处阻塞
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                log.info("{} 执行~", Thread.currentThread().getName());
                //计数器减 1
                countDownLatch.countDown();
            },"t" + i).start();
        }
    }

3、通过Semaphore的方式

通过信号量控制,每个线程在开始执行前需要获取一个许可,执行完毕后释放下一个线程需要的许可。

java 复制代码
    /**
     * 只让第一个信号量有一个许可,另外两个信号量没有许可
     * 1、第一个线程获取到许可,执行打印,然后释放下一个信号量的许可
     * 2、第二个线程获取到许可,执行打印,然后释放下一个信号量的许可
     * 3、第三个线程获取到许可,执行打印
     */
    @Test
    void testMethod3(){
        int length = 3;
        List<Semaphore> semaphoreList = new ArrayList<>();
        for (int i = 1; i <= length; i++) {
            Semaphore semaphore = new Semaphore(i == 1 ? 1 : 0);
            semaphoreList.add(semaphore);
        }
        for (int j = 0; j < length; j++) {
            final int theadIndex = j;
            new Thread(() -> {
                try {
                    //获取许可
                    semaphoreList.get(theadIndex).acquire();
                    log.info("{} 执行~", Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    //释放许可
                    if (theadIndex != length - 1) {
                        semaphoreList.get(theadIndex + 1).release();
                    }
                }
            },"t" + (j + 1)).start();
        }
    }

4、CompletableFuture的方式

原理:异步任务链式调用

优势:代码简洁,内置线程池管理

java 复制代码
CompletableFuture.runAsync(() -> System.out.println("t1"))
    .thenRun(() -> System.out.println("t2"))
    .thenRun(() -> System.out.println("t3"))
    .join();  // 阻塞等待全部完成

5、利用线程池

原理:所有任务提交到单线程队列顺序执行

优势:自动管理线程生命周期

注意:实际是同一个线程执行所有任务

java 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("t1"));
executor.submit(() -> System.out.println("t2"));
executor.submit(() -> System.out.println("t3"));
executor.shutdown();
相关推荐
防搞活机几秒前
ubuntu 服务器(带NVLink)更新显卡驱动 (巨坑!!)
linux·服务器·深度学习·ubuntu·gpu算力·显卡驱动
不是编程家7 分钟前
Linux第二十二讲:数据链路层 && NAT && 代理服务 && 内网穿透
linux·运维·服务器
---学无止境---7 分钟前
Linux中读写自旋锁rwlock的实现
linux
那个松鼠很眼熟w1 小时前
1.多线程基础概念
java
聪明的笨猪猪1 小时前
Java 集合 “Map(1)”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
看着捉急1 小时前
x86_64 centos7.2 上用aarch64-linux-gnu-gcc4.8.5交叉编译qt5.11.3
linux·运维·qt
懒惰蜗牛2 小时前
Day27 | Java集合框架之List接口详解
java·后端·java-ee·list
nsjqj2 小时前
数据结构:Map 和 Set (二)
java·开发语言·数据结构
迎風吹頭髮2 小时前
UNIX下C语言编程与实践18-UNIX 文件存储原理:目录、i 节点、数据块协同存储文件的过程
java·c语言·unix
Murphy_lx2 小时前
Linux(操作系统)文件系统--对打开文件的管理(C语言层面)
linux·服务器·c语言