手寫阻塞隊列BlockQueue--大廠面試常考題

引言

之前自己的面試經歷老被問到手寫阻塞隊列,當然大概率情況下面試官不會很直白的就讓你實現一個阻塞隊列,這個問題有很多的變種,但是萬變不離其宗,知道了怎麼去實現阻塞,也就會實現阻塞隊列了。

比如我曾經遇到的面試題:

  • 百度一面:兩個線程實現交叉打印1-10000
  • 菜鳥二面:要是讓你用數組實現一個阻塞隊列該怎麼實現(ArrayBlockQueue)
  • 快手一面:手寫阻塞隊列的add 和take方法

雖然工作中大都是通過使用JUC包中的類來實現咱們的阻塞隊列需求,但是瞭解其原理還是相當重要的。

下面參考上帝愛喫蘋果的博客實現一個阻塞隊列

public class ZerahBlockQueue {
    //隊列容器
    private List<Integer> container = new ArrayList<>();
    private Lock lock = new ReentrantLock();
    //Condition
    //  隊列爲空
    private Condition isNull = lock.newCondition();
    // 隊列已滿
    private Condition isFull = lock.newCondition();
    private volatile int size;
    private volatile int capacity;

    ZerahBlockQueue(int cap) {
        this.capacity = cap;
    }
        
    public void add(int data) {
        try {
            lock.lock();
            try {
                while (size >= capacity) {
                    System.out.println("隊列已滿,釋放鎖,等待消費者消費數據");
                    isFull.await();
                }
            } catch (InterruptedException e) {
                isFull.signal();
                e.printStackTrace();
            }
            ++size;
            container.add(data);
            isNull.signal();
        } finally {
            lock.unlock();
        }
    }

    public int take(){
        try {
            lock.lock();
            try {
                while (size == 0){
                    System.out.println("阻塞隊列空了,釋放鎖,等待生產者生產數據");
                    isNull.await();
                }
            }catch (InterruptedException e){
                isFull.signal();
                e.printStackTrace();
            }
            --size;
            int res = container.get(0);
            container.remove(0);
            isFull.signal();
            return res ;
        }finally {
            lock.unlock();
        }
    }


}

測試:

public class MyBlockQueueTest {
    public static void main(String[] args) {
        ZerahBlockQueue queue = new ZerahBlockQueue(5);
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                queue.add(i);
                System.out.println("拉個:" + i);
                try {
                    Thread.sleep(500);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for(;;){
                System.out.println("屎殼郎開始工作,消費:" + queue.take());
                try {
                    Thread.sleep(800);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

部分結果:

屎殼郎開始工作,消費:0
拉個:1
屎殼郎開始工作,消費:1
拉個:2
拉個:3
屎殼郎開始工作,消費:2
拉個:4
屎殼郎開始工作,消費:3
拉個:5
拉個:6
屎殼郎開始工作,消費:4
拉個:7
屎殼郎開始工作,消費:5
拉個:8
拉個:9
屎殼郎開始工作,消費:6
拉個:10
拉個:11
屎殼郎開始工作,消費:7
拉個:12
屎殼郎開始工作,消費:8
拉個:13
隊列已滿,釋放鎖,等待消費者消費數據
屎殼郎開始工作,消費:9
拉個:14
隊列已滿,釋放鎖,等待消費者消費數據
屎殼郎開始工作,消費:10

一定要重視,一定要重視,一定要重視!!!

參考:

1.java 併發隊列 BlockingQueue:
https://javadoop.com/post/java-concurrent-queue

2.詳解Condition的await和signal等待/通知機制
https://juejin.im/post/5aeea5e951882506a36c67f0在這裏插入代碼片

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