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

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

近日去一个知名互联网企业参加面试,以前准备多多信心满满,可是面试一开始就是一道不起眼的编程题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毫秒,我没有找到合适的方式控制两个同时运行的线程的顺序,若是你有什么好方法但愿也能分享出来。记得有个朋友告诉我,要想不断提升本身就去面试吧,即便你不想换工做,在面试中确实能发现本身的不足和薄弱的地方。

相关文章
相关标签/搜索