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

这篇具有很好参考价值的文章主要介绍了高频面试题:如何分别用三种姿势实现三个线程交替打印0到100。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近面试遇到的一道题,需要三个线程交替打印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();
            }
        }
    }
}

高频面试题:如何分别用三种姿势实现三个线程交替打印0到100,java,多线程,java,jvm,开发语言,多线程

 文章来源地址https://www.toymoban.com/news/detail-686142.html

 

 

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,这样可以单独唤醒和等待。

高频面试题:如何分别用三种姿势实现三个线程交替打印0到100,java,多线程,java,jvm,开发语言,多线程

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方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁

 

 

到了这里,关于高频面试题:如何分别用三种姿势实现三个线程交替打印0到100的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 多线程知识:三个线程如何交替打印ABC循环100次

    本文博主给大家讲解一道网上非常经典的多线程面试题目。关于三个线程如何交替打印ABC循环100次的问题。 下文实现代码都基于Java代码在单个JVM内实现。 给定三个线程,分别命名为A、B、C,要求这三个线程按照顺序交替打印ABC,每个字母打印100次,最终输出结果为: 推荐博

    2024年02月12日
    浏览(99)
  • 【Java面试题】线程创建的三种方式及区别?

    继承Thread类,子类重写run()方法,调用子类的strat()启动线程。 实现Runnable接口,实现run()方法,调用对象start()启动线程。 实现Callable接口,实现call()方法,用FutureTask()封装实现类。使用FutureTask对象作为Thread对象调用start()启动线程,调用FutureTask对象的get()

    2024年02月12日
    浏览(49)
  • 如何保证三个线程按顺序执行?不会我教你

    👨‍🎓作者:bug菌 ✏️博客:CSDN、掘金、infoQ、51CTO等 🎉简介:CSDN|阿里云|华为云|51CTO等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金 | InfoQ | 51CTO等社区优质创作者,全网粉丝合计 15w+   ;硬核微信公众号「猿圈奇妙屋」,欢迎你的

    2024年02月07日
    浏览(47)
  • 面试大厂不知如何写项目?GitHub上爆火的高频面试真题+6大商城项目 限时分享(1)

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新Java开发全套学习资料》,

    2024年04月25日
    浏览(40)
  • 信创麒麟操作系统卸载docker,并分别用在线、yum、rpm三种方式安装信创的docker

    备注:操作前建议对机器打快照备份,或者备份好数据,如未使用,第一次部署的情况可直接操作 一、卸载DataEase自带的docker 二、安装信创系统的docker 1、联通外网 2、未通网,有yum源 3、未联通外网,无yum源通过rpm安装部署docker 1)确认当前系统的版本 如图: 2)根据系统在

    2024年02月13日
    浏览(128)
  • 126、高频Redis面试题:如何保证Redis和数据库数据一致性

    问题:如果数据库中的某条数据放入缓存后,又马上被更新了,那我们应该如何更新缓存 缺点: 如果先更新缓存成功,在更新数据库的时候失败,这时候会导致数据不一致;缓存的作用是不是临时将我们数据保存在内存,便于提高查询速度;但是如果某条数据在数据库中都

    2024年02月13日
    浏览(43)
  • Python高频面试题——如何在字符串中删除指定字符,掌握strip()、replace()和re.sub ()正确使用方法!

    关于python删除字符串是面试python测试开发工程师的一个经典问题。问题很简单,但是一下子就能测试出来被面试者是否能够熟练的进行python相关编码工作! 对于有些临时抱佛脚的同学来讲,一看删除,很自然就说用remove 、del相关方法,听到这里,就知道面试者根本不知道这

    2024年02月08日
    浏览(48)
  • Java 多线程实现的三种方式

    Java 多线程实现方式主要有三种:继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。 1、继承 Thread 类实现多线程 继承 Thread 类的方法尽管被我列为一种多线程

    2023年04月27日
    浏览(54)
  • Java 实现多线程的三种方式

    1、三种方法的介绍和比较 1、1三种方式得介绍 1、继承Thread类 2、实现Runnable接口 3、实现Callable接口 1、2三种方法的介绍和比较 1、2、1、实现Runnable接口相比继承Thread类有如下优势 1、增强程序的健壮性,将业务逻辑与线程调度分离 2、线程池只能放入实现Runable或Callable类线程

    2024年02月02日
    浏览(47)
  • Python小姿势 - 线程和进程:

    线程和进程: Python里面线程是真正的并行执行,进程是可以并行执行的。 所谓进程,就是操作系统中执行一个程序的独立单元,它是系统进行资源分配和调度的基本单位。一个进程可以创建和撤销另一个进程,同一个进程内可以并发执行多个不同的子程序。 所谓线程,是进

    2024年02月01日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包