资讯专栏INFORMATION COLUMN

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

BaronZhang / 468人阅读

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

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

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

</>复制代码

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

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

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

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

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

</>复制代码

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

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

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

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

下面是具体实现

自旋锁

Java代码

</>复制代码

  1. package interview;
  2. import java.util.concurrent.atomic.AtomicBoolean;
  3. public class PrintNumAndChar1 {
  4. public static void main(String[] args) {
  5. AtomicBoolean isNum = new AtomicBoolean(true);
  6. int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  7. char[] chars = { "a", "b", "c", "d", "e" };
  8. new PrintNums(nums, isNum).start();
  9. new PrintChars(chars, isNum).start();
  10. }
  11. public static class PrintNums extends Thread {
  12. private int[] nums;
  13. private AtomicBoolean isNum;
  14. public PrintNums(int[] a1, AtomicBoolean isNum) {
  15. this.nums = a1;
  16. this.isNum = isNum;
  17. }
  18. public void run() {
  19. int count = 0;
  20. for (int i = 0; i < nums.length; i++) {
  21. while (!isNum.get()) {
  22. Thread.yield();
  23. }
  24. System.out.print(nums[i]);
  25. count++;
  26. if (count == 2) {
  27. isNum.set(false);
  28. count = 0;
  29. }
  30. }
  31. isNum.set(false);
  32. }
  33. }
  34. public static class PrintChars extends Thread {
  35. private char[] chars;
  36. private AtomicBoolean isNum;
  37. public PrintChars(char[] a2, AtomicBoolean isNum) {
  38. this.chars = a2;
  39. this.isNum = isNum;
  40. }
  41. public void run() {
  42. int count = 0;
  43. for (int i = 0; i < chars.length; i++) {
  44. while (isNum.get()) {
  45. Thread.yield();
  46. }
  47. System.out.print(chars[i]);
  48. count++;
  49. if (count == 1) {
  50. isNum.set(true);
  51. count = 0;
  52. }
  53. }
  54. isNum.set(true);
  55. }
  56. }
  57. }

`

LockSupport(直接等待和恢复)

Java代码

</>复制代码

  1. package interview;
  2. import java.util.concurrent.locks.LockSupport;
  3. public class PrintNumAndChar2 {
  4. public static void main(String[] args) {
  5. int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  6. char[] chars = { "a", "b", "c", "d", "e" };
  7. PrintNums t1 = new PrintNums(nums);
  8. PrintChars t2 = new PrintChars(chars);
  9. t1.setPrintChars(t2);
  10. t2.setPrintNums(t1);
  11. t1.start();
  12. t2.start();
  13. }
  14. public static class PrintNums extends Thread {
  15. private int[] nums;
  16. private PrintChars printChars;
  17. public PrintNums(int[] a1) {
  18. super();
  19. this.nums = a1;
  20. }
  21. public void setPrintChars(PrintChars printChars) {
  22. this.printChars = printChars;
  23. }
  24. public void run() {
  25. int count = 0;
  26. for (int i = 0; i < nums.length; i++) {
  27. if(count==2){
  28. count = 0;
  29. LockSupport.unpark(printChars);
  30. LockSupport.park();
  31. }
  32. System.out.print(nums[i]);
  33. count++;
  34. }
  35. LockSupport.unpark(printChars);
  36. }
  37. }
  38. public static class PrintChars extends Thread {
  39. private char[] chars;
  40. private PrintNums printNums;
  41. public PrintChars(char[] chars) {
  42. super();
  43. this.chars = chars;
  44. }
  45. public void setPrintNums(PrintNums printNums) {
  46. this.printNums = printNums;
  47. }
  48. public void run() {
  49. LockSupport.park();
  50. int count = 0;
  51. for (int i = 0; i < chars.length; i++) {
  52. if(count==1){
  53. count = 0;
  54. LockSupport.unpark(printNums);
  55. LockSupport.park();
  56. }
  57. System.out.print(chars[i]);
  58. count++;
  59. }
  60. LockSupport.unpark(printNums);
  61. }
  62. }
  63. }

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

Java代码

</>复制代码

  1. package interview;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class PrintNumAndChar3 {
  6. public static void main(String[] args) {
  7. int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  8. char[] chars = { "a", "b", "c", "d", "e" };
  9. Lock canPrint = new ReentrantLock();
  10. Condition printNum = canPrint.newCondition();
  11. Condition printChar = canPrint.newCondition();
  12. new PrintNums(nums, canPrint, printNum, printChar).start();
  13. new PrintChars(chars, canPrint, printNum, printChar).start();
  14. }
  15. public static class PrintNums extends Thread {
  16. private int[] nums;
  17. private Condition printNum;
  18. private Condition printChar;
  19. private Lock canPrint;
  20. public PrintNums(int[] nums, Lock canPrint, Condition printNum, Condition printChar) {
  21. super();
  22. this.nums = nums;
  23. this.printNum = printNum;
  24. this.printChar = printChar;
  25. this.canPrint = canPrint;
  26. }
  27. public void run() {
  28. int count = 0;
  29. try {
  30. for (int n : nums) {
  31. if (count == 2) {
  32. canPrint.lock();
  33. count = 0;
  34. printChar.signal();
  35. printNum.await();
  36. canPrint.unlock();
  37. }
  38. System.out.print(n);
  39. count++;
  40. }
  41. canPrint.lock();
  42. printChar.signal();
  43. canPrint.unlock();
  44. } catch (InterruptedException e) {
  45. // TODO Auto-generated catch block
  46. e.printStackTrace();
  47. }
  48. }
  49. }
  50. public static class PrintChars extends Thread {
  51. private char[] chars;
  52. private Condition printNum;
  53. private Condition printChar;
  54. private Lock canPrint;
  55. public PrintChars(char[] chars, Lock canPrint, Condition printNum, Condition printChar) {
  56. super();
  57. this.chars = chars;
  58. this.printNum = printNum;
  59. this.printChar = printChar;
  60. this.canPrint = canPrint;
  61. }
  62. public void run() {
  63. int count = 0;
  64. try {
  65. Thread.sleep(100);
  66. for (char n : chars) {
  67. if (count == 1) {
  68. canPrint.lock();
  69. count = 0;
  70. printNum.signal();
  71. printChar.await();
  72. canPrint.unlock();
  73. }
  74. System.out.print(n);
  75. count++;
  76. }
  77. canPrint.lock();
  78. printNum.signal();
  79. canPrint.unlock();
  80. } catch (InterruptedException e) {
  81. // TODO Auto-generated catch block
  82. e.printStackTrace();
  83. }
  84. }
  85. }
  86. }

使用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元查看
<