Java老掉牙的面试问题:线程交替打印问题,分析实操一下 上

简介: Java老掉牙的面试问题:线程交替打印问题,分析实操一下 上


一个老掉牙的java面试问题 , 多线程交替打印。

有打印 ABC 的, 有打印 123 的, 有打印到100的 。

其实都一样。

ps: 最近好多小伙伴问这个,这个题这么热门么?

实例实战思路

拿一个来做示例, 就交替打印ABC. (文末也说下从1到100的)

一起看看这个小题目 :

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

主角

三个线程 线程A  线程 B 线程 C

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

要做的事

交替打印  A B C

那就是 线程A 负责打印 A ,线程 B 负责打印 B ,线程C 负责打印 C 。

简单分析

A线程打印完, B线程 打印 ;

B线程打印完 ,C 线程打印。

也就是说,这3个线程 ABC ,有顺序/协作 。

那么这三个家伙 怎么知道自己要打印东西呢?

那必然是通知 等待

思路

三个线程都 准备就绪, 准备大展身手。

接到携带暗号的通知(默认暗号为 A) -> 核对通知的暗号 -> 打印->然后修改暗号-> 发出携带暗号通知(让别的线程认领)

如果接到通知,发现暗号不对,怎么办呢? 继续进入等待,等待下个通知的到来。

简图

三个线程的一举一动 :

ps:

  • 如果是线程A打印完,就把通知暗号改成B,并发出通知;
  • 如果是线程B打印完,就把通知暗号改成C,并发出通知;
  • 同样,如果是线程C打印完,就把通知改回A,继续发通知。

代码

写个ReentrantLock条件控制来搞一下。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class DoTest {
    //控制三个线程 ABC,保证同一时刻只有一个线程工作
    private static Lock lock = new ReentrantLock(true);
    //  Condition ,控制等待或是 通知
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();
    //默认的通知 暗号 A
    private static String  CODE= "A";
    public static void main(String[] args) {
        Thread A = new Thread(() -> {
            while (true) {
                try {
                    printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread B = new Thread(() -> {
            while (true) {
                try {
                    printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread C = new Thread(() -> {
            while (true) {
                try {
                    printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        A.start();
        B.start();
        C.start();
    }
    public static void printA() throws InterruptedException {
        //等待
        lock.lock();
        try {
            //核对
            while (!CODE.equals("A")) {
                //暗号不对,就进入等待
                conditionA.await();
            }
            System.out.println("A");
            //改暗号,通知B
            CODE = "B";
            conditionB.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public static void printB() throws InterruptedException {
        lock.lock();
        try {
            while (!CODE.equals("B")) {
                conditionB.await();
            }
            System.out.println("B");
            //改暗号,通知C
            CODE = "C";
            conditionC.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public static void printC() throws InterruptedException {
        lock.lock();
        try {
            while (!CODE.equals("C")) {
                conditionC.await();
            }
            System.out.println("C");
            //改暗号,通知A
            CODE = "A";
            conditionA.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

效果:

可以看到,三个线程 ABC 都开始 无休止的进行了 等待 -接通知 -核对- 打印-改暗号发通知 。

当然如果需要他们不这么无休止,只需要 做一个标识进行判断就好,例如 加在一起已经打印够100次了,就停止 之类的限制值。

相关文章
|
6天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
21 2
|
11天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
16天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
13天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
38 4
|
13天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
54 4
|
19天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
37 2
|
20天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
13 2
|
26天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
53 5
|
25天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
19 1
|
23天前
|
算法 Java
JAVA 二叉树面试题
JAVA 二叉树面试题
14 0