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

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


一个关于多线程协作的题目经常会出现在大厂的面试中:有三个线程分别打印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索引失效的场景大搜罗

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

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

原文始发于微信公众号(互联网全栈架构):高频面试题:多线程顺序打印ABC字符20次

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/173659.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!