资讯专栏INFORMATION COLUMN

一道多线程面试题引起的自我救赎

BaronZhang / 395人阅读

摘要:重温一个面试题内容数组内容为数组内容为个英文字母,使用两个线程分别输入两个数组,打印内容为这样的规律提取一下核心内容,去除次要内容两个线程需要交替执行,打印数字的线程需要先执行,数组打印完毕后线程需要结束。

一道多线程面试题引起的自我救赎

近日去一个知名互联网企业参加面试,之前准备多多信心满满,但是面试一开始就是一道不起眼的编程题

数组A内容为 1,2,3,4...52 ,数组B内容为26个英文字母,使用两个线程分别输入两个数组,
打印内容为:12a34b56c78e....... 这样的规律

当时看了一下觉得so easy, 第一思路就是使用wait()/notify() 通过判断已打印的数量让两个线程交替等待。
但是装逼情绪一下来了,突然想起了没怎么使用的CyclicBarrier ,直观认为这种线程闩也能等待,还能计数
估计也能解决这个问题,于是开始设计算法,思考了很久,在纸上也推演逻辑,可怎么也想不出来,突然有种
直觉我肯定没理解透CyclicBarrier的原理,当时时间已经很紧张了,这道题就这样被我的装逼情绪给毁了,
情绪已经受到了影响,之后的面试可想而知。

CyclicBarrier 字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。
就像赛马场上所有骑手都准备就位后才开始起跑一样,把这类用于解决上面的面试题完全不合适。:<

回到家里越想越气,明明几道题可以回答好却因为第一道题影响情绪,进入了防御思维方式,不能很好的发挥自己。为了惩罚,我要自己用三种解法解决上面那道面试题。

好吧,进入解决的正题。
重温一个面试题内容:

数组A内容为 1,2,3,4...52 ,数组B内容为26个英文字母,使用两个线程分别输入两个数组,
打印内容为:12a34b56c78e....... 这样的规律

提取一下核心内容,去除次要内容
两个线程需要交替执行,打印数字的线程需要先执行,数组打印完毕后线程需要结束。

转换成模型,可以理解为 数字线程先执行,字母线程先等待,每次打印相当于一个子任务,任务完毕后
通知另一个线程工作,自己进入等待状态,如此交替往复直到子任务全部完毕,再次通知彼此以防对方卡住。

转换成Java中的组件,可以让线程停下/启动的方式有如下几种: suspend/resume(已废弃),wait/notify(需要锁对象有点浪费) 或者 Lock/Condition, LockSupport(非常好直接等待和恢复),自旋锁(对于这个场景也不错)

下面是具体实现

自旋锁

Java代码

package interview;

import java.util.concurrent.atomic.AtomicBoolean;

public class PrintNumAndChar1 {

    public static void main(String[] args) {
        AtomicBoolean isNum = new AtomicBoolean(true);
        int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        char[] chars = { "a", "b", "c", "d", "e" };
        new PrintNums(nums, isNum).start();
        new PrintChars(chars, isNum).start();

    }

    public static class PrintNums extends Thread {
        private int[] nums;
        private AtomicBoolean isNum;

        public PrintNums(int[] a1, AtomicBoolean isNum) {
            this.nums = a1;
            this.isNum = isNum;
        }

        public void run() {
            int count = 0;
            for (int i = 0; i < nums.length; i++) {
                while (!isNum.get()) {
                    Thread.yield();
                }
                System.out.print(nums[i]);
                count++;
                if (count == 2) {
                    isNum.set(false);
                    count = 0;
                }
            }
            isNum.set(false);
        }
    }

    public static class PrintChars extends Thread {
        private char[] chars;
        private AtomicBoolean isNum;

        public PrintChars(char[] a2, AtomicBoolean isNum) {
            this.chars = a2;
            this.isNum = isNum;
        }

        public void run() {
            int count = 0;
            for (int i = 0; i < chars.length; i++) {
                while (isNum.get()) {
                    Thread.yield();
                }
                System.out.print(chars[i]);
                count++;
                if (count == 1) {
                    isNum.set(true);
                    count = 0;
                }
            }
            isNum.set(true);
        }
    }
}

`

LockSupport(直接等待和恢复)

Java代码

package interview;

import java.util.concurrent.locks.LockSupport;

public class PrintNumAndChar2 {

    public static void main(String[] args) {
        int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        char[] chars = { "a", "b", "c", "d", "e" };
        PrintNums t1 = new PrintNums(nums);
        PrintChars t2 = new PrintChars(chars);
        t1.setPrintChars(t2);
        t2.setPrintNums(t1);
        t1.start();
        t2.start();

    }

    public static class PrintNums extends Thread {
        private int[] nums;
        private PrintChars printChars;

        public PrintNums(int[] a1) {
            super();
            this.nums = a1;
        }

        public void setPrintChars(PrintChars printChars) {
            this.printChars = printChars;
        }

        public void run() {
            int count = 0;
            for (int i = 0; i < nums.length; i++) {
                if(count==2){
                    count = 0;
                    LockSupport.unpark(printChars);
                    LockSupport.park();
                }
                System.out.print(nums[i]);
                count++;
            }
            LockSupport.unpark(printChars);
        }
    }

    public static class PrintChars extends Thread {
        private char[] chars;
        private PrintNums printNums;

        public PrintChars(char[] chars) {
            super();
            this.chars = chars;
        }

        public void setPrintNums(PrintNums printNums) {
            this.printNums = printNums;
        }

        public void run() {
            LockSupport.park();
            int count = 0;
            for (int i = 0; i < chars.length; i++) {
                if(count==1){
                    count = 0;
                    LockSupport.unpark(printNums);
                    LockSupport.park();
                }
                System.out.print(chars[i]);
                count++;
            }
            LockSupport.unpark(printNums);
        }
    }
}

wait/notify(需要锁对象有点浪费) 或者 Lock/Condition ,我认为最渣的实现

Java代码

package interview;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintNumAndChar3 {

    public static void main(String[] args) {
        int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        char[] chars = { "a", "b", "c", "d", "e" };
        Lock canPrint = new ReentrantLock();
        Condition printNum = canPrint.newCondition();
        Condition printChar = canPrint.newCondition();
        new PrintNums(nums, canPrint, printNum, printChar).start();
        new PrintChars(chars, canPrint, printNum, printChar).start();
    }

    public static class PrintNums extends Thread {
        private int[] nums;
        private Condition printNum;
        private Condition printChar;
        private Lock canPrint;

        public PrintNums(int[] nums, Lock canPrint, Condition printNum, Condition printChar) {
            super();
            this.nums = nums;
            this.printNum = printNum;
            this.printChar = printChar;
            this.canPrint = canPrint;
        }

        public void run() {
            int count = 0;
            try {
                for (int n : nums) {
                    if (count == 2) {
                        canPrint.lock();
                        count = 0;
                        printChar.signal();
                        printNum.await();
                        canPrint.unlock();
                    }
                    System.out.print(n);
                    count++;
                }
                canPrint.lock();
                printChar.signal();
                canPrint.unlock();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public static class PrintChars extends Thread {
        private char[] chars;
        private Condition printNum;
        private Condition printChar;
        private Lock canPrint;

        public PrintChars(char[] chars, Lock canPrint, Condition printNum, Condition printChar) {
            super();
            this.chars = chars;
            this.printNum = printNum;
            this.printChar = printChar;
            this.canPrint = canPrint;
        }

        public void run() {
            int count = 0;
            try {
                Thread.sleep(100);
                for (char n : chars) {
                    if (count == 1) {
                        canPrint.lock();
                        count = 0;
                        printNum.signal();
                        printChar.await();
                        canPrint.unlock();
                    }
                    System.out.print(n);
                    count++;
                }
                canPrint.lock();
                printNum.signal();
                canPrint.unlock();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

使用Lock锁的方式有个问题,我使用了sleep 让打印字符的线程等待了100毫秒,我没有找到合适的方式控制两个同时运行的线程的顺序,如果你有什么好方法希望也能分享出来。
记得有个朋友告诉我,要想不断提高自己就去面试吧,即使你不想换工作,在面试中确实能发现自己的不足和薄弱的地方。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65067.html

相关文章

  • 「今日头条」前端面试和思路解析

    摘要:一篇文章和一道面试题最近,有篇名为张图帮你一步步看清和的执行顺序的文章引起了我的关注。作者用一道年今日头条的前端面试题为引子,分步讲解了最终结果的执行原因。从字面意思理解,让我们等等。当前的最新版本,在这里的执行顺序上,的确存在有问题。 一篇文章和一道面试题 最近,有篇名为 《8张图帮你一步步看清 async/await 和 promise 的执行顺序》 的文章引起了我的关注。 作者用...

    宠来也 评论0 收藏0
  • 三年Java后端面试经历

    摘要:前言三年后端开发经验,面的目标岗位是的高级后端开发。面试结束,应该没有后续。 前言 三年Java后端开发经验,面的目标岗位是20k-35k的高级后端Java开发。 第一场,基本裸面,关于曾经的项目部门答的不好,所以还是得好好准备。 某C轮在线旅游公司 笔试 先做半个小时的笔试题,一共六个题目,两道go语言的基础题,一道斐波那契相关,一道数据库行列转置,一道实现一个栈,还有一道是百万计...

    darry 评论0 收藏0
  • 一道面试引起思考

    摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...

    xiaoqibTn 评论0 收藏0
  • 一道面试引起思考

    摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...

    snowLu 评论0 收藏0

发表评论

0条评论

BaronZhang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<