Java多线程轮流打印ABC的四种实现方式

 更新时间:2025年08月06日 09:41:16   作者:Mylvzi  
在Java中实现多线程轮流打印ABC的操作是一种常见的并发编程练习,它展示了如何利用多线程提高程序执行的效率和并行性,本文将深入探讨如何用Java实现这一任务,需要的朋友可以参考下

在多线程编程中,一个经典的面试题是:

启动三个线程,分别打印 A、B、C,要求按顺序轮流输出,如 ABCABCABC… 重复若干次。

这个问题看似简单,但涉及线程间的协作与通信。本文将从入门到进阶,系统讲解 4 种主流解法,并附带完整可运行的源码。

实现目标

假设每个线程负责打印一个字符:

  • 线程 A 打印 A
  • 线程 B 打印 B
  • 线程 C 打印 C

期望输出如下格式:

ABCABCABCABCABC

循环打印若干次(如 5 次)。

方法一:synchronized + wait/notifyAll

思路解析

使用一个共享变量 state 表示当前轮到哪个线程。每个线程进入临界区后判断是否是自己该打印的时机,否则调用 wait() 挂起,等待唤醒。

示例代码

/*
 1.设置共享变量state控制线程的执行顺序
 2.在打印方法中传入两个参数:打印的内容, 当前线程的state
 3.每个线程在调用打印方法时都要传入自己对应的顺序,A:0,B:1,C:2
*/
public class PrintABC {
    private static int state = 0;// 设置共享变量控制线程执行顺序
    private static final int COUNT = 5;// 控制执行顺序
    private static Object lock = new Object();// 用于加锁的对象

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                printChar("A", 0);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                printChar("B", 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                printChar("C", 2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }

    private static void printChar(String name, int curState) throws InterruptedException {
        int i = 0;
        while(i < COUNT) {
            synchronized (lock) {
                if(state % 3 == curState) {// 是当前线程的执行顺序
                    System.out.print(name + " ");
                    i++;// 控制当前线程的执行的次数
                    state++;// 轮转到下一个线程
                    lock.notifyAll();
                }else {// 轮不到当前线程  wait等待
                    lock.wait();
                }
            }
        }
    }
}

优缺点

  • ✅ 简单易懂,容易上手;
  • 使用全局的共享变量state来控制线程的执行顺序
  • ❌ 效率较低,notifyAll() 会唤醒所有线程。

方法二:ReentrantLock + Condition

思路解析

相比 synchronizedReentrantLock 提供更灵活的线程调度机制,Condition 可以精准唤醒目标线程,避免不必要的唤醒。

示例代码

    /*
     *第四种方法:ReentrantLock + Condition 来实现更加精确的线程间通信
     * ReentrantLock实现加锁,解锁; Condition实现线程间的通信
     */

    public static final int COUNT = 5;
    public static int state = 0;
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition conA = lock.newCondition();
    public static Condition conB = lock.newCondition();
    public static Condition conC = lock.newCondition();

    public static void main(String[] args) {
        new Thread(() ->printChar("A", 0, conA, conB)).start();
        new Thread(() ->printChar("B", 1, conB, conC)).start();
        new Thread(() ->printChar("C", 2, conC, conA)).start();
    }

    private static void printChar(String name, int curState, Condition curCondition, Condition nextCondition) {
        for(int i = 0; i < COUNT; i++) {
            lock.lock();
                try {
                    while(state % 3 != curState)
                        curCondition.await();

                    // 是当前线程  执行
                    System.out.print(name);
                    state++;
                    nextCondition.signal();

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    lock.unlock();
                }

        }
    }

优缺点

  • ✅ 支持精准唤醒,性能优于 notifyAll
  • ❌ 编写稍复杂,需要手动释放锁。

方法三:Semaphore 信号量控制

思路解析

使用 3 个信号量 semAsemBsemC 控制线程谁可以打印,线程执行后释放下一个信号量即可。

示例代码

    /*
     * 第二种方法:使用Semaphore 信号量的方式控制执行顺序
     * 如何保证先打印A:semA, semB, semC  将A的许可设置为1,B,C的许可设置为0,则一定先执行A
     * 如何保证打印顺序:在打印方法中传入三个参数:name, curSem, nextSem  A-B  B-C  C-A
     * 执行完当前线程打印内容之后,让下一个线程release一个许可
     */

    private static final Semaphore semA = new Semaphore(1);
    private static final Semaphore semB = new Semaphore(0);
    private static final Semaphore semC = new Semaphore(0);
    private static final int COUNT = 5;

    public static void main(String[] args) {
        new Thread(() -> printChar("A", semA, semB)).start();
        new Thread(() -> printChar("B", semB, semC)).start();
        new Thread(() -> printChar("C", semC, semA)).start();
    }

    private static void printChar(String name, Semaphore cur, Semaphore next) {
        for(int i = 0; i < COUNT;) {
            try {
                cur.acquire();
                System.out.print(name + " ");
                ++i;
                next.release();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

优缺点

  • ✅ 信号机制清晰,逻辑明确;
  • ❌ 不支持灵活的线程增删;

方法四:BlockingQueue 队列传令

思路解析

为每个线程分配一个阻塞队列,当队列有“令牌”时线程执行,执行完毕后将令牌交给下一个队列。
阻塞队列是一个线程安全的队列

  1. 队列为空时,take会阻塞
  2. 队列为满时,put会阻塞

示例代码

    /*
     * 第三种方法:使用BlockingQueue作为令牌的方式来控制打印顺序
     * 创建三个队列,每个队列分别打印对应需要打印的内容
     * 使用令牌来控制打印的顺序,和使用semaphore类似
     * 在打印的方法中传入三个参数:要打印的内容,当前队列,下一个队列
     */

    public static BlockingQueue<String> qA = new ArrayBlockingQueue<>(1);
    public static BlockingQueue<String> qB = new ArrayBlockingQueue<>(1);
    public static BlockingQueue<String> qC = new ArrayBlockingQueue<>(1);
    public static final int COUNT = 5;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> printChar("A", qA, qB)).start();
        new Thread(() -> printChar("B", qB, qC)).start();
        new Thread(() -> printChar("C", qC, qA)).start();

        qA.put("go");
    }

    private static void printChar(String name, BlockingQueue<String> curQueue, BlockingQueue<String> nextQueue) {
        for(int i = 0; i < COUNT; i++) {
            try {
                curQueue.take();// 等待令牌传递
                System.out.print(name + " ");
                nextQueue.put("go");// 传递令牌
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

优缺点

  • ✅ 队列阻塞机制天然适合线程通信;
  • ❌ 每个线程都需独立队列,稍显繁琐。

使用Semaphore和BlockingQueue的方式其实很像;对于阻塞队列来说,是通过传递令牌的方式来交接接力棒

总结对比

方法控制方式唤醒机制难度推荐场景
synchronized状态 + 模 3 判断notifyAll简单测试、学习入门
ReentrantLock状态 + Conditionsignal⭐⭐更精确唤醒,推荐实际开发使用
Semaphore信号量控制顺序release/acquire⭐⭐控制有限资源访问/固定顺序
BlockingQueue令牌传递take/put⭐⭐结构直观,适合理解通信流程

写在最后

线程按顺序轮流执行是实际开发中很常见的需求,比如:生产者消费者模型、有序打印日志、顺序处理任务等。

掌握以上几种方法不仅能应对面试题,更能提升对 Java 并发编程的理解。

以上就是Java多线程轮流打印ABC的四种实现方式的详细内容,更多关于Java多线程轮流打印ABC的资料请关注脚本之家其它相关文章!

相关文章

  • Spring MVC如何设置响应

    Spring MVC如何设置响应

    本文介绍了如何在Spring框架中设置响应,并通过不同的注解返回静态页面、HTML片段和JSON数据,此外,还讲解了如何设置响应的状态码和Header
    2025-01-01
  • Java中对象的创建和销毁过程详析

    Java中对象的创建和销毁过程详析

    这篇文章主要介绍了Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机制负责,文中介绍的非常详细,需要的朋友可以参考下
    2025-02-02
  • Java基础异常处理代码及原理解析

    Java基础异常处理代码及原理解析

    这篇文章主要介绍了java基础异常处理代码及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Jmeter安装及配置教程详解

    Jmeter安装及配置教程详解

    很多朋友私信小编Jmeter安装及配置教程能出一期教程吗?正巧赶上疫情,不是太忙,下面小编把Jmeter安装及配置教程分享到脚本之家平台,感兴趣的朋友跟随小编一起看看吧
    2021-12-12
  • Maven项目中引入本地JAR包的两种常见方式

    Maven项目中引入本地JAR包的两种常见方式

    在开发Java项目时我们经常会遇到需要引入外部本地jar包的情况,这篇文章主要介绍了Maven项目中引入本地JAR包的两种常见方式,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-06-06
  • MyBatis 管理和查找TypeHandler的方法

    MyBatis 管理和查找TypeHandler的方法

    MyBatis通过TypeHandlerRegistry组件集中管理和查找TypeHandler,TypeHandler用于处理Java类型和JDBC类型之间的转换,本文介绍MyBatis 管理和查找TypeHandler的方法,感兴趣的朋友一起看看吧
    2025-03-03
  • Spring Boot 整合单机websocket的步骤 附github源码

    Spring Boot 整合单机websocket的步骤 附github源码

    websocket 是一个通信协议,通过单个 TCP 连接提供全双工通信,这篇文章主要介绍了Spring Boot 整合单机websocket的步骤(附github源码),需要的朋友可以参考下
    2021-10-10
  • Jtable和JTree的写法示例代码

    Jtable和JTree的写法示例代码

    这篇文章主要介绍了Jtable和JTree的基本概念,常用构造方法,以及二者的通用写法,需要的朋友可以了解下。
    2017-09-09
  • nacos客户端一致性hash负载需求实现

    nacos客户端一致性hash负载需求实现

    这篇文章主要介绍了nacos客户端一致性hash负载的需求实现过程及步骤详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • RabbitMQ消费者限流实现消息处理优化

    RabbitMQ消费者限流实现消息处理优化

    这篇文章主要介绍了RabbitMQ消费者限流实现消息处理优化,消费者限流是用于消费者每次获取消息时限制条数,注意前提是手动确认模式,并且在手动确认后才能获取到消息,感兴趣想要详细了解可以参考下文
    2023-05-05

最新评论