[大白裝逼]java阻塞隊列

前言

java提供了很多阻塞隊列,在平時的開發中也會使用到,所以在此對java提供的阻塞隊列進行一個瞭解總結

首先

java的阻塞隊列都繼承與BlockingQueue,其有共同的方法
boolean offer(Object o);//將數據o加入隊列中,加入成功返回true,失敗則爲false,此方法不阻塞
boolean offer(Object o,long timeout,TimeUnit unit);//將o加入隊列中,若timeout過後爲未加入成功返回false,否則返回true,此方法會阻塞等待,unit爲時間單位
put(Object o);//將數據o加入隊列中,若隊列沒有空間則會阻塞當前的線程進行等待
Object poll();//取走隊列頭部的數據,如取不出則返回null
Object poll(long timeout,TimeUnit unit);//取走隊列頭部的數據,若取不出則等待timeout,等待後取不出返回null
Object take();//取出隊列首部數據,若取不出則阻塞直到取出來
int drainTo(Collection c);//取出隊列中所有數據放到容器c中,其中的排序爲隊列中的排序,返回取出的數量
int drainTo(Collection c,int maxLength);//取出隊列的數據,最大數量爲maxLength,返回實際獲取得數量
其他隊列基本都有實現以下的方法:
peek()與poll()功能一樣
隊列有add()方法,其內部實現與put()基本都是一樣的.而且還有許多方法與列表方法一樣用的,如size(),remove(),clear(),遍歷時使用迭代器遍歷.其中remove()爲刪除隊頭
隊列的有界無界表明其是否指定或限定隊列大小

ArrayBlockingQueue

用數組實現的有界阻塞隊列,按照先進先出的原則.

public class ArrayBlockingQueueTest {
    ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(5,true);//定義隊列大小爲5,隊列爲先進先出順序,false爲未指定順序
    ExecutorService executorService = Executors.newSingleThreadExecutor();//單線程的線程池

    public ArrayBlockingQueueTest() {
        for (int i = 0;i<6;i++){
            put("數據:"+i);//放入數據
        }
    }

    public void put(String data){
        try {
            arrayBlockingQueue.put(data);//向隊列中放入數據
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        handle();//開啓線程處理
    }

    public void handle(){
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("處理中...");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String data = null;
                try {
                    data = arrayBlockingQueue.poll(1, TimeUnit.SECONDS);//取出數據,1秒內取不出返回null
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if ( data != null) {
                    System.out.println(data+"處理結束...");
                }else {
                    System.out.println("無數據處理...");
                }
            }
        });
    }
}

LinkedBlockingQueue

基於鏈表的阻塞隊列,按照先進先出,其加入隊列與取出隊列的線程使用獨立的鎖來控制同步,所以其有更高的併發效率,需要注意的是在初始化時如果不指定長度會默認爲無限長,這有可能會佔用較多的資源,使用方法與ArrayBlockingQueue一致.

PriorityBlockingQueue

無界優先級隊列,默認爲升序,與Arrays.sort()方法排序類似,在初始化時可以指定其初始長度,默認爲11,增長數量爲當大於等於64長度時,爲原長度的1.5被,當小於64的時候爲原長度的2倍+2.初始化時可設置其排序的比較規則Comparator(),也可重寫其compareTo方法.其無法排序同優先級


public class PriorityBlockingQueueTest {
    PriorityBlockingQueue<String> priorityBlockingQueue;
    String[] strings;

    Comparator<String> mComparator = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o2.length() - o1.length();
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    };
    public PriorityBlockingQueueTest() {
        strings = new String[]{"666", "6626", "6645457645661234566", "6612423564566", "6644564564564564564564566", "664566", "664564566", "664566"};
        priorityBlockingQueue = new PriorityBlockingQueue<>(11,mComparator);
        priorityBlockingQueue.put("666");
        priorityBlockingQueue.put("6626");
        priorityBlockingQueue.put("6645457645661234566");
        priorityBlockingQueue.put("6612423564566");
        priorityBlockingQueue.put("6644564564564564564564566");
        priorityBlockingQueue.put("664566");
        priorityBlockingQueue.put("664564566");
        priorityBlockingQueue.put("664566");

        Iterator<String> iterator = priorityBlockingQueue.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("-----------------------------------------");
        Arrays.sort(strings,mComparator);
        for (String data : strings){
            System.out.println(data);
        }
    }


}

運行結果
這裏寫圖片描述

經試驗驗證,其內部的排序使用二分法排序,如下代碼,結果與想象的不一樣,具體原因還有待研究

 private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
                                       Comparator<? super T> cmp) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;//取中間值比較
            Object e = array[parent];
            if (cmp.compare(x, (T) e) >= 0)//此處遇到返回大於等於0的就退出,退出操作有點問題
                break;
            array[k] = e;
            k = parent;
        }
        array[k] = x;
    }

DelayQueue

延時取出的無界隊列,基於PriorityQueue實現的,加入的元素需要實現Delayed接口,目前並不會使用

SynchronousQueue

不存儲元素的隊列,當插入的一個元素,必須等待其他線程移除纔可繼續運行,當移除一個元素,如使用remove且無插入在等待會報錯,使用take()會等到有元素插入了才返回,否則阻塞,其他的有就有沒有就返回null.

public class SynchronoutsQueueTest {
    SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
    public SynchronoutsQueueTest() {
        new MyThread().start();
        try {
            stringSynchronousQueue.put("666");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("----------putEndMain----------");
        try {
            System.out.println("----------poll:"+stringSynchronousQueue.take()+"Main----------");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("----------poll:"+stringSynchronousQueue.poll()+"Thread----------");
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                stringSynchronousQueue.put("666");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("----------putEndThread----------");
        }
    }
}

運行結果
這裏寫圖片描述

LinkedTransferQueue

無界的鏈表隊列,用於生產者等待消費者的需求,其實現了TransferQueue接口,其中有幾個重要方法
transfer(Object o);//加入隊列,如加入時沒有取出隊列操作,會阻塞等待取出.
boolean tryTransfer(Object o);//加入隊列,如有取出的操作在等待則加入並返回true,否則不加入並返回false
boolean tryTransfer(Object o,long timeout,TimeUnit unit);//有超時機制的tryTransfer

public class LinkedTransferQueueTest {
    LinkedTransferQueue<String> linkedTransferQueue = new LinkedTransferQueue<>();
    public LinkedTransferQueueTest() {
        new TestThread().start();
        while (true) {
            try {
                Thread.sleep(3000);
                System.out.println("消費:" + linkedTransferQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class TestThread extends Thread{
        @Override
        public void run() {
            while (true) {
                try {
                    System.out.println("出產...");
                    linkedTransferQueue.transfer("34636");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

運行結果
這裏寫圖片描述
在每次出產後都會等待消費後再進行下一次的出產

LinedBlockingDeque

雙向阻塞隊列,在初始化時可設置其最大容量,默認爲int的最大值,隊列提供了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法進行對隊頭和隊尾的操作。

小結

java提供的阻塞隊列使用起來也是挺便利的,雖然有些可能寫錯了,希望大家指出糾正.

個人博客:https://lewis-v.github.io/

公衆號 個人公衆號

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