近日去一個知名互聯網企業參加面試,以前準備多多信心滿滿,可是面試一開始就是一道不起眼的編程題java
數組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....... 這樣的規律this
提取一下核心內容,去除次要內容
兩個線程須要交替執行,打印數字的線程須要先執行,數組打印完畢後線程須要結束。atom
轉換成模型,能夠理解爲 數字線程先執行,字母線程先等待,每次打印至關於一個子任務,任務完畢後
通知另外一個線程工做,本身進入等待狀態,如此交替往復直到子任務所有完畢,再次通知彼此以防對方卡住。線程
轉換成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毫秒,我沒有找到合適的方式控制兩個同時運行的線程的順序,若是你有什麼好方法但願也能分享出來。記得有個朋友告訴我,要想不斷提升本身就去面試吧,即便你不想換工做,在面試中確實能發現本身的不足和薄弱的地方。