一道多線程面試題引發的自我救贖

一道多線程面試題引發的自我救贖

近日去一個知名互聯網企業參加面試,以前準備多多信心滿滿,可是面試一開始就是一道不起眼的編程題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

  1. 提取一下核心內容,去除次要內容
    兩個線程須要交替執行,打印數字的線程須要先執行,數組打印完畢後線程須要結束。atom

  2. 轉換成模型,能夠理解爲 數字線程先執行,字母線程先等待,每次打印至關於一個子任務,任務完畢後
    通知另外一個線程工做,本身進入等待狀態,如此交替往復直到子任務所有完畢,再次通知彼此以防對方卡住。線程

  3. 轉換成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毫秒,我沒有找到合適的方式控制兩個同時運行的線程的順序,若是你有什麼好方法但願也能分享出來。記得有個朋友告訴我,要想不斷提升本身就去面試吧,即便你不想換工做,在面試中確實能發現本身的不足和薄弱的地方。

相關文章
相關標籤/搜索