blockingQueue實現消費-生產模式

blockingQueue實爲阻塞隊列,數據由消費者線程、生產者線程共享。消費者產生數據放入阻塞隊列,倘若阻塞隊列已滿,則生產者線程被阻塞直到消費者線程取出數據或者是直接返回失敗,若隊列數據爲空,則消費者線程阻塞直到生產者線程放入數據或者直接返回失敗。且阻塞隊列的數據爲先進先出的規則。

常用api

offer(E e) 向阻塞隊列放入數據,倘若阻塞隊列已滿,則直接返回false。
offer(E e, long timeout, TimeUnit unit) 向阻塞隊列放入數據,倘若timeout時間過後數據還未放入隊列,則直接返回falseput(E e) 向阻塞隊列放入數據,倘若阻塞隊列已滿,則生產線程阻塞直到消費線程取出數據。
poll() 向阻塞隊列取出數據,倘若阻塞隊列沒有數據,則直接返回false。
poll(long timeout, TimeUnit unit) 向阻塞隊列取出數據,倘若timeout時間過後依然沒有取出數據,則直接返回false。
take() 向阻塞隊列放入數據,倘若阻塞隊列已滿,則消費線程阻塞直到生產線程放入數據。
drainTo():一次性從阻塞隊列所有可用的數據對象。

blockingQueue常用實現類

ArrayBlockingQueue 採用數組來放入存儲數據。在初始化的時候需要指定數組長度。這就限定了ArrayBlockingQueue 只能存儲指定長度的數據。並且ArrayBlockingQueue 的放入與取出不能併發進行,因爲ArrayBlockingQueue 採用唯一的鎖來維護存放隊列的數組。適合生產線程產生的數據快於消費線程消費數據的場景。
LinkedBlockingQueue 採用鏈表來維護數據隊列。因爲鏈表增加與取出數據對象是作用在不同對象上,所以取出與增加是可以併發進行的。但是LinkedBlockingQueue 初始化的時候可以不限定長度,這就導致了LinkedBlockingQueue 可以存儲接近於無窮的數據,這可能會導致內存溢出。所有LinkedBlockingQueue 適合生產線程產生的數據慢於消費線程消費數據的場景。

阻塞隊列使用場景

列如用戶上傳了一個數據量很大的文件,後臺解析需要耗費十幾秒,這時總不能讓用戶等待十幾秒吧,這時候只需將文件數據放入阻塞隊列,然後放回給用戶成功,接着生產線程去處理這些數據。

例如12306開票的時候大量用戶湧進來買票,這時候由於高併發量太大,導致很多業務邏輯需要同時處理,數據庫io操作過於密集頻繁,這時候可以把用戶的請求數據放入消息隊列進行排隊,相關接口去取數據用於業務操作。

例如12306訂票成功後需要發送郵件通知給用戶,由於12306的系統能應對很強大的高併發場景,但是郵件系統不能處理大量高併發。由於發送郵件可以延遲,所有隻需要把發送郵件的內容放入阻塞隊列,發郵件線程去取數據發送即可。

使用ArrayBlockingQueue實現消費-生產情景
package TestExample;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;

public class TestExample{

ArrayBlockingQueue<String> test = new ArrayBlockingQueue<String>(10);

CountDownLatch latch = new CountDownLatch(2);

@Test
public void test(){

    ExecutorService service = Executors.newFixedThreadPool(2);
    service.execute(new ProductTread(test));
    service.execute(new ConsumeTread(test));
    try {
        latch.await();
        System.out.println("主線程結束!");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

class ProductTread implements Runnable{

    BlockingQueue<String> bq;

    public void run() {
        for(int i=0;i<20;i++){
            try {
                bq.put(String.valueOf(i));
                System.out.println("生產者放入數據:"+String.valueOf(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        latch.countDown();
    }

    ProductTread(BlockingQueue<String> bq){
        this.bq=bq;
    }
}

class ConsumeTread implements Runnable{

        BlockingQueue<String> bq;

        public void run() {
            for(int i=0;i<20;i++){
                try {
                    Thread.sleep(1000);
                    System.out.println("消費者取出數據"+bq.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            latch.countDown();
        }

        ConsumeTread(BlockingQueue<String> bq){
            this.bq=bq;
        }
    }

}

運行結果:

生產者放入數據:0
生產者放入數據:1
生產者放入數據:2
生產者放入數據:3
生產者放入數據:4
生產者放入數據:5
生產者放入數據:6
生產者放入數據:7
生產者放入數據:8
生產者放入數據:9
消費者取出數據0
生產者放入數據:10
消費者取出數據1
生產者放入數據:11
消費者取出數據2
生產者放入數據:12
消費者取出數據3
生產者放入數據:13
消費者取出數據4
生產者放入數據:14
消費者取出數據5
生產者放入數據:15
消費者取出數據6
生產者放入數據:16
消費者取出數據7
生產者放入數據:17
消費者取出數據8
生產者放入數據:18
消費者取出數據9
生產者放入數據:19
消費者取出數據10
消費者取出數據11
消費者取出數據12
消費者取出數據13
消費者取出數據14
消費者取出數據15
消費者取出數據16
消費者取出數據17
消費者取出數據18
消費者取出數據19
主線程結束!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章