摘要:重温一个面试题内容数组内容为数组内容为个英文字母,使用两个线程分别输入两个数组,打印内容为这样的规律提取一下核心内容,去除次要内容两个线程需要交替执行,打印数字的线程需要先执行,数组打印完毕后线程需要结束。
一道多线程面试题引起的自我救赎
近日去一个知名互联网企业参加面试,之前准备多多信心满满,但是面试一开始就是一道不起眼的编程题
数组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 的执行顺序》 的文章引起了我的关注。 作者用...
摘要:前言三年后端开发经验,面的目标岗位是的高级后端开发。面试结束,应该没有后续。 前言 三年Java后端开发经验,面的目标岗位是20k-35k的高级后端Java开发。 第一场,基本裸面,关于曾经的项目部门答的不好,所以还是得好好准备。 某C轮在线旅游公司 笔试 先做半个小时的笔试题,一共六个题目,两道go语言的基础题,一道斐波那契相关,一道数据库行列转置,一道实现一个栈,还有一道是百万计...
摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...
摘要:这个时候查了下正则表达式的文档文档点击这里,发现有一个方法,可以返回匹配成功的结果。那么我来总结下文章想表达的内容对于具有固定格式的字符串,可以考虑使用正则表达式来识别和匹配。 今天在认真干(划)活(水)的时候,看到群里有人发了一道头条的面试题,就顺便看了一下,发现挺有意思的,就决定分享给大家,并且给出我的解决方案和思考过程。 题目如下: 实现一个get函数,使得下面的调用可以输出正确...
阅读 3257·2021-10-11 10:59
阅读 2811·2021-10-11 10:58
阅读 2243·2021-09-04 16:45
阅读 2716·2019-08-30 15:44
阅读 670·2019-08-30 15:44
阅读 3198·2019-08-30 10:51
阅读 1596·2019-08-29 18:46
阅读 2748·2019-08-29 13:57