高频面试题:多线程顺序打印ABC字符20次

news2024/11/25 0:44:39

676cddbf94f42d48988b15b65a330588.gif

一个关于多线程协作的题目经常会出现在大厂的面试中:有三个线程分别打印A、B、C,请让这三个线程按顺序打印出ABC20次。

我们知道,线程调度机制是非确定性的,如果不加上额外的并发控制,直接启动三个线程,那么这几个线程的执行顺序是不确定的,打印出来的字符也不一定是按照ABC这样的顺序来显示。同时,题目不光要求按顺序打印出ABC,而且还要打印20次,也就是这样的形式:ABCABCABC...,当然,打印30次或者100次什么的,代码基本上一样,修改一下循环的次数即可。

实现这样的功能有很多方法,我们使用一种简洁方便的方式来完成,同时,为了加深印象和理解,我们尝试采用层层递进的方式来进行讲解,也就是:

  1. 启动三个线程,打印出ABC,但无序,可能是BCA,或者CBA等等。

  2. 启动三个线程,按照顺序打印出ABC,要确定每次运行都按这个顺序打印。

  3. 启用三个线程,按照顺序打印出ABC,不光要按照顺序打印,而且要连续打印20次,也就是ABCABC...这样的形式。

一、打印字符ABC,但无序

这个比较简单,三个线程各打印A、B、C,然后启动这些线程即可,当然,打印出来的字符就是没有顺序的了。代码如下:

package com.sample.interview.multithread;
// 三个线程打印字符ABC,但顺序不确定
public class PrintRandomly {
    public static void main(String[] args) {
        Thread threadA = new Thread(()-> System.out.print("A"));
        Thread threadB = new Thread(()-> System.out.print("B"));
        Thread threadC = new Thread(()-> System.out.print("C"));
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

二、按顺序打印字符ABC

按顺序打印字符ABC,那我们就得控制这三个线程的执行顺序,线程A先执行,接下来是线程B,最后是线程C。针对类似的并发编程场景,Java提供了一个非常方便实用的类Semaphore,也就是信号量,可以将它看作一个“许可证”,通过它可以实现多线程对于公共资源的并发访问控制,一个线程在获取资源时需要先取得一个许可,否则会阻塞,资源使用完成后,再把许可放回去。

为了按顺序打印ABC,我们控制线程B必须在线程A执行之后再开始执行,线程C类似,所以我们定义三个信号量,初始的许可证数量都是0,因为数量都是0,所以调用信号量的acquire方法时会被阻塞,如果前面的线程执行完成,把许可证数量加1,后面的线程就可以执行了,这样,就实现了顺序打印的功能。

package com.sample.interview.multithread;
import java.util.concurrent.Semaphore;
// 多线程顺序打印出ABC
public class PrintSequentially {
    public static void main(String[] args) {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(0);

        Thread threadA = new Thread(() -> {
            try {
                semaphoreA.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("A");
            semaphoreB.release();
        });

        Thread threadB = new Thread(() -> {
            try {
                semaphoreB.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("B");
            semaphoreC.release();
        });

        Thread threadC = new Thread(() -> {
            try {
                semaphoreC.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("C");
            semaphoreA.release();

        });
        // 线程A的许可证数量加1
        semaphoreA.release();
        threadC.start();
        threadB.start();
        threadA.start();
    }
}

三、按顺序打印字符ABC20次

有了第二步的铺垫,要实现顺序打印字符ABC20次的功能就相对比较简单了,在每个线程里面直接循环20次即可。因为信号量的acquire方法会尝试去获取一个许可,如果没有就会阻塞,如果拿到了,就把信号量的许可减1,而release方法则是把许可加1,所以,如果前面的线程没有调用这个归还方法,后面的线程是会阻塞的,多次循环也就是简单地把这样的运作多次执行即可。我们把代码稍作提炼如下:

package com.sample.interview.multithread;
import java.util.concurrent.Semaphore;
// 循环打印ABC20次
public class PrintSequentiallyLoop {
    public static void main(String[] args) {
        Semaphore semaphoreA = new Semaphore(1);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(0);

        PrintChar printCharA = new PrintChar(semaphoreA, semaphoreB,"A");
        PrintChar printCharB = new PrintChar(semaphoreB, semaphoreC,"B");
        PrintChar printCharC = new PrintChar(semaphoreC, semaphoreA,"C");

        new Thread(printCharA).start();
        new Thread(printCharB).start();
        new Thread(printCharC).start();
    }

    static class PrintChar implements Runnable{
        Semaphore cur;
        Semaphore next;
        private String str;
        private int times = 20;

        public PrintChar(Semaphore cur, Semaphore next, String str){
            this.cur = cur;
            this.next = next;
            this.str = str;
        }

        @Override
        public void run() {
            for (int i = 0; i < times; i++) {
                try {
                    cur.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print(str);
                next.release();
            }
        }
    }
}

以上就是多线程顺序打印ABC20次的解决方案,前面也提过,有很多方法可以解决该问题,在当前案例中,我们使用在信号量Semaphore作为并发控制的手段,它的功能强大,使用简单,也便于记忆,后续如果碰到类似的面试题目,我想,只要认真读完本文,是一定能够给出答案的。

都看到这里了,请帮忙一键三连啊,也就是点击文末的在看、点赞、分享,这样会让我的文章让更多人看到,也会大大地激励我进行更多的输出,谢谢!

推荐阅读:

一网打尽:MySQL索引失效的场景大搜罗

这个设计模式的用法,一般人我不告诉他

《论语》是很多公司取名的源泉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904357.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软件测试】Java和Python做自动化测试哪个更有优势?

Java和Python做自动化测试&#xff0c;哪个更有优势&#xff1f;这两个语言都是很流行的语言&#xff0c;所以从技术上很难说谁好谁不好的。因为要说好不好得看使用的环境和要求。就像生活在中国&#xff0c;你天天说英语&#xff0c;别人会说你“又拽鸟语啊”&#xff1f;但是…

Postman的高级用法—Runner的使用​

1.首先在postman新建要批量运行的接口文件夹&#xff0c;新建一个接口&#xff0c;并设置好全局变量。 2.然后在Test里面设置好要断言的方法 如&#xff1a; tests["Status code is 200"] responseCode.code 200; tests["Response time is less than 10000…

接口自动化中如何完成接口加密与解密?

加密是一种限制对网络上传输数据的访问权的技术。将密文还原为原始明文的过程称为解密&#xff0c;它是加密的反向处理。在接口开发中使用加密、解密技术&#xff0c;可以防止机密数据被泄露或篡改。在接口自动化测试过程中&#xff0c;如果要验证加密接口响应值正确性的话&…

简单认识镜像底层原理详解和基于Docker file创建镜像

文章目录 一、镜像底层原理1.联合文件系统(UnionFS)2.镜像加载原理3.为什么Docker里的centos的大小才200M? 二、Dockerfile1.简介2.Dockerfile操作常用命令 三、创建Docker镜像1.基于已有镜像创建2.基于本地模板创建3.基于Dockerfile创建4.Dockerfile多阶段构建镜像 一、镜像底…

卷积神经网络全解:(AlexNet/VGG/ GoogLeNet/LeNet/ResNet/卷积/激活/池化/全连接)、现代卷积神经网络、经典卷积神经网络

CNN&#xff0c;卷积神经网络&#xff0c;Convolution Neural Network 卷积计算公式&#xff1a;N &#xff08;W-F2p&#xff09;/s1 这个公式每次都得看看&#xff0c;不能忘 1 经典网络 按照时间顺序 1.1 LeNet LeNet是 Yann LeCun在1998年提出&#xff0c;用于解决手…

Ribbon:负载均衡及Ribbon

什么是负载均衡&#xff1f; 第一种轮询算法&#xff0c;依次遍历去执行&#xff0c;达到负载均衡 集成Ribbon 导入pom&#xff0c;在消费者服务里的pom文件导入 <!-- Ribbon 集成 --><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spr…

《合成孔径雷达成像算法与实现》Figure3.12

clc clear close all% 参数设置 TBP 724; % 时间带宽积 T 42e-6; % 脉冲持续时间 t_0 1e-6; % 脉冲回波时延 Nfft 2^11; % fft长度% 参数计算 B TBP/…

第8步---MySQL的存储过程和触发器

第8步---MySQL的存储过程和触发器 1.存储过程 5开始支持的 sql集&#xff0c;类似Java中的代码中的方法 实现对sql的封装和服用 有输入和输出 可以声明变量 可以实现一下复杂的控制语句 1.1入门案例 基本语法 测试数据 -- 创建表的测试数据 create table dept(deptno int pri…

jstack(Stack Trace for Java)Java堆栈跟踪工具

jstack&#xff08;Stack Trace for Java&#xff09;Java堆栈跟踪工具 jstack&#xff08;Stack Trace for Java&#xff09;命令用于生成虚拟机当前时刻的线程快照&#xff08;一般称为threaddump或者javacore文件&#xff09;。 线程快照就是当前虚拟机内每一条线程正在执…

Linux:iptables SNAT与DNAT

目录 一、SNAT 1.1 SNAT原理与应用 1.2 SNAT转换前提条件 1.3 SNAT工作原理 1.4 SNAT实例 二、DNAT 2.1DNAT原理与应用 2.2 DNAT转换前提条件 2.2实例 一、SNAT 1.1 SNAT原理与应用 SNAT 应用环境:局域网主机共享单个公网IP地址接入Internet (私有IP不能在Internet中正…

JAVAEE免费工程师教程之springboot综合案例

day04_springboot综合案例 用户管理 查询用户 查询所有用户执行流程 编写UserMapper接口 public interface UserMapper {// 查询所有用户List<UserInfo> findAllUsers(); }编写UserService public interface UserService extends UserDetailsService {/*** 查询所有…

(2023,UniversalFakeDetect)跨生成模型泛化的通用假图像检测器

Towards Universal Fake Image Detectors that Generalize Across Generative Models 公众号&#xff1a;EDPJ 目录 0. 摘要 1. 简介 2. 相关工作 3. 基础 3.1 问题设置 3.2 分析为什么先前的工作无法泛化 4. 方法 5. 实验 5.1 研究生成模型 5.2 真假分类基线 5.3 …

三重奏的和谐:如何完美对齐公司、部门与个人目标

引言 在企业的运营和管理中&#xff0c;目标的设定与对齐是至关重要的。它不仅决定了公司的方向和愿景&#xff0c;还影响到每一个部门和团队成员的工作内容和效果。如何确保公司目标、部门目标和团队个人目标之间的完美对齐&#xff0c;是每一个管理者都需要面对的挑战。 目…

HCIP学习--STP

在交换机上的线路冗余会产生的问题 昨天讲到了一个冗余的概念&#xff0c;下面就这个冗余引出来的问题来记录今天的内容 线路的冗余对于路由器来岁意味择可以选择更多的路线&#xff0c;但是对于交换机来说可不是啥好事情 比如下图假设A下面有一台设备要发送一个广播&#x…

第二讲:BeanFactory的实现

BeanFactory的实现 1. 环境准备2. 初始化DefaultListableBeanFactory3. 手动注册BeanDefinition4. 手动添加后置处理器5. 获取被依赖注入的Bean对象6. 让所有的单例bean初始化时加载7. 总结 Spring 的发展历史较为悠久&#xff0c;因此很多资料还在讲解它较旧的实现&#xff0c…

ROS与STM32通信(二)-pyserial

文章目录 下位机上位机自定义msg消息发布订阅 ROS与STM32通信一般分为两种&#xff0c; STM32上运行ros节点实现通信使用普通的串口库进行通信&#xff0c;然后以话题方式发布 第一种方式具体实现过程可参考上篇文章ROS与STM32通信-rosserial&#xff0c;上述文章中的收发频率…

一例Vague病毒的分析

这是一例通过U盘传播的文件夹病毒&#xff0c;有收集用户文件的行为&#xff0c;但是&#xff0c;没有回传和远控行为&#xff0c;有点奇怪&#xff0c;其中的字符串进行了加密。 样本比较简单&#xff0c;使用IDA很容易就看明白了。 根据匹配到威胁情报&#xff0c;有叫Vague蠕…

阿里云服务器和轻量云服务器对比有什么区别?

阿里云轻量应用服务器和云服务器ECS有什么区别&#xff1f;ECS是专业级云服务器&#xff0c;轻量应用服务器是轻量级服务器&#xff0c;轻量服务器使用门槛更低&#xff0c;适合个人开发者或中小企业新手使用&#xff0c;可视化运维&#xff0c;云服务器ECS适合集群类、高可用、…

初识SD绘画

最近sd绘画可谓是火的一塌糊涂&#xff0c;AI的绘画能力是真强大。废话不多说&#xff0c;直入主题。 1&#xff0c;sd的本地安装大体有2种&#xff0c;一种是官网安装&#xff0c;一种是秋叶大神的整合包。我安装的是秋叶大神的包&#xff0c;里面的插件和模型都有了&#xf…

Personalize Segment Anything Model with One Shot【论文翻译】

​ 论文基础信息如下 https://arxiv.org/pdf/2305.03048.pdfhttps://github.com/ZrrSkywalker/Personalize-SAM Abstract 通过大数据预训练驱动&#xff0c;分段任意模型&#xff08;Segment Anything Model&#xff0c;SAM&#xff09;已被证明是一个强大且可提示的框架&am…