高频面试题:如何分别用三种姿势实现三个线程交替打印0到100

news2024/10/6 2:30:10

最近面试遇到的一道题,需要三个线程交替打印0-100,当时对多线程并不是很熟悉因此没怎么写出来,网上搜了之后得到现

synchronized + wait/notifyAll

实现思路:判断当前打印数字和线程数的取余,不等于当前线程则处于等待状态。循环结束唤醒所有等待线程。

public class PrintExample {
    //创建一个公共锁对象
    private static final Object Lock = new Object();
    //执行线程数
    private static final int THREAD_COUNT = 3;

    //打印数字的起始点
    private static volatile int START = 0;

    //打印数字的结束点
    private static final int END = 100;

    private static class Print implements Runnable{
        private final int index;

        public Print(int index){
            this.index = index;
        }


        @Override
        public void run() {
            while(START<END){
                synchronized (Lock){
                    //START和线程数进行取余,如果不等于当前线程的则等待
                    while(START % THREAD_COUNT != index){
                        try{
                            Lock.wait();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    //否则进行输出
                    if(START<=END){
                        System.out.println("Thread" + (index+1) +  ",打印结果:" + START);
                    }
                    START++;
                    //唤醒等待线程
                    Lock.notifyAll();
                }
            }
        }

        public static void main(String[] args) {
            for(int i = 0; i < THREAD_COUNT; i++){
                new Thread(new Print(i)).start();
            }
        }
    }
}

ReetrantLock + await/signalAll

实现思路:实现方式和synchronized + wait/notifyAll儿乎完全一样。我们只需要4步:
1.synchronized 替换为ReentrantLock

2.根据锁对象创建一个Condition对象

3.wait替换成await

4.notifyAll 替换为 signalAll
 

public class PrintExample {
    //创建一个公共锁对象
    private static final ReentrantLock Lock = new ReentrantLock();

    //根据锁对象创建一个Condition对象
    private static final Condition CONDITION = Lock.newCondition();

    //执行线程数
    private static final int THREAD_COUNT = 3;

    //打印数字的起始点
    private static volatile int START = 0;

    //打印数字的结束点
    private static final int END = 100;

    private static class Print implements Runnable{
        private final int index;

        public Print(int index){
            this.index = index;
        }


        @Override
        public void run() {
            while(START<END){
                Lock.lock();
                try {
                    //START和线程数进行取余,如果不等于当前线程的则等待
                    while(START % THREAD_COUNT != index){
                        try{
                            CONDITION.await();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    //否则进行输出
                    if(START<=END){
                        System.out.println("Thread" + (index+1) +  ",打印结果:" + START);
                    }
                    START++;
                    //唤醒等待线程
                    CONDITION.signalAll();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    Lock.unlock();
                }
                
                }
            }
            
        public static void main(String[] args) {
            for(int i = 0; i < THREAD_COUNT; i++){
                new Thread(new Print(i)).start();
            }
        }
    }
}

ReetrantLock + await/signal

因为Condition相对wait/notify方式,可以唤醒指定线程。那我们就完全不用每次都唤醒全部线程,仅需要唤醒下一次需要执行的线程就可以了。
相比较 ReentrantLock + await/signalAll 改进方法:
1.去除公共的Condition对象,替换为List<Condition> conditions;
2.调用"下一个线程的"Condition对象的signal方法唤醒下一个线程;

public class PrintExample {
    //创建一个公共锁对象
    private static final ReentrantLock Lock = new ReentrantLock();

    //根据锁对象创建一个Condition对象
    //private static final Condition CONDITION = Lock.newCondition();

    //执行线程数
    private static final int THREAD_COUNT = 3;

    //打印数字的起始点
    private static volatile int START = 0;

    //打印数字的结束点
    private static final int END = 100;

    private static class Print implements Runnable{
        private final int index;
        private final List<Condition> conditions;

        public Print(int index,List<Condition> conditions){
            this.index = index;
            this.conditions = conditions;
        }

        //只唤醒下一个线程
        private void signalNext(){
            int nextIndex = (index + 1) % THREAD_COUNT;
            conditions.get(nextIndex).signal();
        }

        @Override
        public void run() {
            while(START<END){
                Lock.lock();
                try {
                    //START和线程数进行取余,如果不等于当前线程的则等待
                    while(START % THREAD_COUNT != index){
                        try{
                            conditions.get(index).await();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    //否则进行输出
                    if(START<=END){
                        System.out.println("Thread" + (index+1) +  ",打印结果:" + START);
                    }
                    START++;
                    //唤醒等待线程
                    signalNext();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    Lock.unlock();
                }

                }
            }

        public static void main(String[] args) {
            List<Condition> conditionList = new ArrayList<>();
            conditionList.add(Lock.newCondition());
            conditionList.add(Lock.newCondition());
            conditionList.add(Lock.newCondition());
            for(int i = 0; i < THREAD_COUNT; i++){
                new Thread(new Print(i,conditionList)).start();
            }
        }
    }
}

此处使用 List<Condition> conditions让每个线程都拥有属于自己的condition,这样可以单独唤醒和等待。

Condition是什么

概念:

condition可以理解为条件队列。当一个线程在调用了其await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现

方法:

Condition依赖于Lock接口

方法解释
lock.newCondition()生成一个Condition
await()对应Object的wait();使线程等待
signal()对应Object的notify();唤醒线程

注意:调用Condition的await()和signal()方法,都必须在lock.lock()和lock.unlock()之间使用

在生产者和消费者中Condition的执行方式:

  • 当在线程Consumer中调用await方法后,线程Consumer将释放锁,并且将自己沉睡,等待唤醒。
  • 这时等到线程Producer获取到锁后,开始执行任务,完毕后,调用Condition的signalall方法,唤醒线程Consumer,线程Consumer恢复执行。

以上说明Condition是一个多线程间协调通信的工具类,使得某个或某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁

 

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

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

相关文章

前端:横向滚动条,拖动进行左右滚动(含隐藏滚动条)

效果 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, i…

【算法系列篇】位运算

文章目录 前言什么是位运算算法1.判断字符是否唯一1.1 题目要求1.2 做题思路1.3 Java代码实现 2. 丢失的数字2.1 题目要求2.2 做题思路2.3 Java代码实现 3. 两数之和3.1 题目要求3.2 做题思路3.3 Java代码实现 4. 只出现一次的数字4.1 题目要求4.2 做题思路4.3 Java代码实现 5.…

数据库范式以及drop、delete 与 truncate区别

数据库范式了解吗? 我们从一个数据库设计的初始化阶段开始, 但是并没有规范化设计的背景知识 而要求我们设计一个员工管理系统, 可能得到表结构如下: 数据异常 首先这个表有如下问题: 1. 数据冗余: 我们可以看到部门名称相同时, 部门地址也是重复的 因此会重复存储数据 …

【UE5】给模型指定面添加自定义材质

实现步骤 1. 首先我们向UE中导入一个简单的模型&#xff0c;可以看到目前该模型的材质插槽只有一个&#xff0c;当我们修改材质时会使得模型整体的材质全部改变&#xff0c;如果我们只想改变模型的某些面的材质就需要继续做后续操作。 2. 选择建模模式 3. 在模式工具栏中点击…

手机改图片文字软件有哪些?简单分享这几款

手机改图片文字软件有哪些&#xff1f;现在有很多手机APP可以帮助我们实现图片中文字的提取和修改&#xff0c;但是其中一些工具可能会缺乏一些必要的功能&#xff0c;或者不太适合某些特定的用途。在这篇文章中&#xff0c;我们将介绍几款非常实用的手机改图片文字软件。 第一…

复杂性管理与重复性管理

在前面我们说到了所谓的"计算机科学", 重点在于如何控制大型系统的复杂性. 复杂性本身当然也是个很大的话题, 而一种常见的复杂性的来源则是重复性, 即是由不断的重复所带来的复杂性. 重复性带来的复杂性常被人忽视, 大概是因为一开始它是不起眼的, 而当人们意识到它…

软技能的重要性:在面试中展示团队合作与沟通能力

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

2023年下半年西安/广州/深圳软考(中/高级)开班啦!!!

软考是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资格考试。 系统集成项…

​LeetCode解法汇总1448. 统计二叉树中好节点的数目

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给你一棵…

初识Java 3-1 控制流

目录 if-else语句 迭代语句 for循环 for-in语法 return break和continue 标签机制 switch 本笔记参考自&#xff1a; 《On Java 中文版》 Java不允许将数字作为布尔值使用&#xff0c;若需要在一些条件语句中使用一个非布尔值&#xff0c;比如if(a)&#xff0c;那么就需…

美团北极星榜单,服务零售的医美新样本

事实证明&#xff0c;任何时候&#xff0c;人们对美的追求都是刚需&#xff0c;只是有时候被压抑了。 德勤中国的《中国医美行业2023年度洞悉报告》&#xff08;以下简称“报告”&#xff09;显示&#xff0c;中国医美市场规模预计在2023年超过2000亿元&#xff0c;实现20%增速…

残差网络、Dropout正则化、Batch Normalization浅了解

残差网络&#xff1a; 为什么需要残差网络&#xff1a; 残差网络的目的是为了解决深度神经网络在训练过程中遇到的退化问题&#xff0c;即随着网络层数的增加&#xff0c;训练集的误差反而增大&#xff0c;而不是过拟合。残差网络的优点有以下几点&#xff1a; 残差网络可以…

上门服务系统|上门服务软件开发|上门服务改善生活质量的便捷之选

随着现代生活的快节奏和社交距离的需求&#xff0c;我们越来越渴望能够以更便捷、高效的方式获得我们所需的服务。为了满足这一需求&#xff0c;我们公司开发了一款创新的上门服务系统&#xff0c;旨在将便利与质量相结合&#xff0c;为您提供无与伦比的体验。 无论您是忙碌的白…

惠普NS1020激光打印机碳粉警告提示及添加碳粉方法

本文也适用于惠普NS1020、1020c 和 1020w 系列打印机。 通过碳粉量指示灯检查碳粉量。 如果碳粉量是满的或指示器显示 1&#xff0c;可选择添加一个碳粉或者忽略不添加。如果碳粉量指示灯显示 2或 2 和碳粉量警告感叹号图标 &#xff0c;则表示碳粉量不足或严重不足&#xff0…

ORACEL 账户被锁定、无监听

现象1&#xff1a;oracle数据库账号被锁定 OA页面情况&#xff1a;OA系统可以正常登录&#xff0c;但是表单查不出数据 PL SQL 连接情况&#xff1a;有明确的提示&#xff0c;oracle账号被锁。 Tomcat控制台情况&#xff1a;有明确提示账号被锁 解决办法 在命令行中输入就可…

批量身份证图片转Excel,核验真伪,保留头像,只需一款软件

你是否曾经遇到过需要将大量员工的身份证图片转化为Excel表格的情况&#xff1f;这种情况可能会让你感到无从下手。但是&#xff0c;现在有了金鸣表格文字识别电脑客户端&#xff0c;一切都将变得轻松便捷。 首先&#xff0c;你只需要前往金鸣识别官网下载并安装金鸣表格文字识…

企业网络设备监控工具

如今&#xff0c;组织在运营业务方面面临着日益激烈的竞争和日益复杂的问题&#xff0c;在这种情况下&#xff0c;拥有以最高效率运行的网络基础设施不再是奢侈品。相反&#xff0c;对于任何希望在各自领域成为领跑者的组织来说&#xff0c;这是必要的。网络基础设施中每个网络…

python爬虫14:总结

python爬虫14&#xff1a;总结 前言 ​ python实现网络爬虫非常简单&#xff0c;只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点&#xff0c;方便以后复习。 申明 ​ 本系列所涉及的代码仅用于个人研究与讨论&#xff0c;并不会对网站产生不好…

查看edge浏览器插件的安装位置

C:\Users\zhang\AppData\Local\Microsoft\Edge\User Data\Default\Extensions 这是我的目录&#xff0c;把中间的的替换成你的电脑用户名就可以了 你也可以先输入目录的部分名称&#xff0c;下拉找对应的目录

docker部署前端项目保姆级教程

本地启动docker&#xff08;有不会启动的吗&#xff1f;下载docker&#xff08;小海豚&#xff09;双击起来就行&#xff09; 准备阿里云账号&#xff08;免费&#xff09; 没有就去注册一个&#xff0c;记住密码后面要用到 官网地址&#xff1a;阿里云登录 - 欢迎登录阿里云…