记一道阿里多线程笔试题

最近有同事收到阿里的面试邀请,因地点在杭州就先进行了一轮线上笔试,其中有这样一道题,和大家分享一下。

题目是这样的:用多个线程交替打印字符,如字符串"ali",一个线程打印a,一个线程打印l,一个线程打印i , 一个线程打印空格,如:ali ali ali …
代码支持拓展,能打印任意字符串"alibaba",如:alibaba alibaba alibaba alibaba …

是一道经典的线程交替打印相关的题目,和网上轮流打印ABC的题目很相似,先说一下轮流打印ABC的解题思路,再回过头来看阿里的这道题。

关键点主要有两个,一是如何让对应的线程打印对应的字符,一是如何控制打印的顺序。
一般的解决思路是用可重入锁 ReentrantLock 加 Condition,代码如下:

public class AlternativePrint {
    private ReentrantLock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    private Condition conditionC = lock.newCondition();
	// 控制打印顺序
    private int number = 1;

    public void loopA(){
        lock.lock();
        try {
        	// 未到打印时机,等待
            if (number != 1){
                conditionA.await();
            }
           
            System.out.print(Thread.currentThread().getName());
		
            number = 2;
            // 唤醒下一个打印线程
            conditionB.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void loopB(){
        lock.lock();
        try {
            if (number != 2){
                conditionB.await();
            }

            System.out.print(Thread.currentThread().getName());

            number = 3;
            conditionC.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void loopC(){
        lock.lock();
        try {
            if (number != 3){
                conditionC.await();
            }

            System.out.print(Thread.currentThread().getName());

            number = 1;
            conditionA.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

测试类:

public static void main(String[] args) {

    AlternativePrint alternativePrint = new AlternativePrint();

    new Thread("A"){
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                alternativePrint.loopA();
            }
        }
    }.start();

    new Thread("B"){
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                alternativePrint.loopB();
            }
        }
    }.start();

    new Thread("C"){
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                alternativePrint.loopC();
            }
        }
    }.start();

}

相信这道题难不倒大家,那回头再来看阿里这道题。这道题不仅需要解决上面两个问题,还需要用字符长度的线程去打印,我的解决思路是这样的:

  • 以字符串长度创建线程,再加一个打印空格的线程
  • 将线程和打印的字符封装成一个对象
  • 为了控制打印的顺序,将对象之间的关系维护成单向循环链表

代码如下:

public class Print {

    private ReentrantLock lock;

    private String str;

    private int printTimes;

    private void print() {
        List<Item> list = new ArrayList<>();
        for (int i = 0; i < str.length(); i++) {
            list.add(new Item(str.charAt(i), printTimes, lock));
        }
        list.add(new Item((char) 32, printTimes, lock));

        for (int i = 0; i < list.size(); i++) {
            Item item = list.get(i);
            if (i == list.size() - 1) {
                item.setNext(list.get(0));
            }
            else {
                item.setNext(list.get(i + 1));
                if (i == 0) {
                    // 激活第一个线程
                    item.setActive(true);
                }
            }
        }

        list.forEach(Thread::start);
    }

    public Print(String str, int printTimes) {
        this.str = str;
        this.printTimes = printTimes;
        this.lock = new ReentrantLock();
    }

    static class Item extends Thread {

        private char ch;

        private int printTimes;

        private ReentrantLock lock;

        private Condition condition;

        private Item next;

        private boolean active;

        @Override
        public void run() {
            for (int i = 0; i < printTimes; i++) {
                lock.lock();
                try {
                    if (!active) {
                        condition.await();
                    }

                    System.out.print(ch);
                    active = false;

                    if (!next.isActive()) {
                        next.setActive(true);
                        next.getCondition().signal();
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
                finally {
                    lock.unlock();
                }
            }
        }

        public Item(char ch, int printTimes, ReentrantLock lock) {
            this.ch = ch;
            this.printTimes = printTimes;
            this.lock = lock;
            this.condition = lock.newCondition();
        }

        public Condition getCondition() { return condition; }

        public Item getNext() { return next; }

        public void setNext(Item next) { this.next = next; }

        public void setActive(boolean active) { this.active = active; }

        public boolean isActive() { return active; }

    }

}

测试类:

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    System.out.print("请输入字符串:");
    String next = input.next();
    System.out.print("请输入打印次数:");
    int printTimes = input.nextInt();

    Print print = new Print(next, printTimes);
    print.print();
}

打印结果:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章