Java并發(fā)編程 - 阻塞隊列(BlockingQueue)

1. 概念和特性

很多線程問題可以使用一個或多個隊列優(yōu)雅而安全地解決惭婿。
比如說不恭,生產(chǎn)者線程向隊列插入元素,消費者線程負責獲取元素财饥。
利用這種方式换吧,我們可以安全地從一個線程向另一個線程傳遞參數(shù)。

阻塞隊列(BlockingQueue)是協(xié)調(diào)多個線程之間合作的有用工具钥星。
當試圖向阻塞隊列添加元素而隊列已滿沾瓦,或者從隊列移出元素而隊列為空的時候,將導致線程阻塞谦炒。

阻塞隊列的應用場景:
工作線程可以周期性地將中間結果存儲在阻塞隊列中贯莺。
其它工作線程移除中間結果襟衰,并進一步進行修改票唆。
這樣,隊列可以自動地平衡負載秀存。

?

2. BlockingQueue的方法

方法 動作 特殊情況
add 添加一個元素 如果隊列滿透且,拋出IllegalStateException
element 返回隊頭元素 如果對列空撕蔼,拋出NoSuchElementException
offer 添加一個元素并返回true 如果隊列滿,則返回false
peek 返回隊頭元素 如果隊列空秽誊,則返回null
poll 移除并返回隊頭元素 如果隊列空鲸沮,則返回null
put 添加一個元素 如果隊列滿,則阻塞
remove 移除并返回隊頭元素 如果對列空锅论,拋出NoSuchElementException
take 移除并返回隊頭元素 如果對列空讼溺,則阻塞

offer和poll方法還可以有超時時間的參數(shù)

// 在100毫秒的時間內(nèi)向隊尾插入一個元素;超時返回false
boolean success = q.offer(x, 100, TimeUnit.MILLISECONDS);
// 在100毫秒的時間內(nèi)移除隊頭元素;超時返回null
Object head = q.poll(100, TimeUnit.MILLISECONDS);

?

3. Java并發(fā)包的阻塞隊列實現(xiàn)

java.util.concurrent包提供了阻塞隊列的幾種實現(xiàn)。

類名 解釋
LinkedBlockingQueue 雙端隊列最易,容量沒有上界怒坯,但是也可以選擇指定一個最大容量。
ArrayBlockingQueue 在構造是需要指定容量藻懒,并且有一個可選的參數(shù)來指定是否需要公平性剔猿。如果設置了公平性,那么等待時間最長的線程會優(yōu)先得到處理嬉荆。公平性會降低性能归敬。
PriorityBlockingQueue 優(yōu)先隊列,元素按照它們的優(yōu)先級順序移除。這個隊列沒有容量上限汪茧。但是隊列為空獲取元素也會造成阻塞椅亚。

?

4. 程序?qū)崙?zhàn)

SearchFileByBlockingQueueDemo.java

package zeus.playground.concurrent.queue;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SearchFileByBlockingQueueDemo {
    private static final int FILE_QUEUE_SIZE = 10;
    private static final int SEARCH_THREADS = 100;
    private static final Path DUMMY = Path.of("");
    private static BlockingQueue<Path> queue = new ArrayBlockingQueue<>(FILE_QUEUE_SIZE);

    public static void main(String[] args) {
        try (var in = new Scanner(System.in)) {
            System.out.print("Enter base directory (e.g. /opt/jdk-9-src): ");
            String directory = in.nextLine();
            System.out.print("Enter keyword (e.g. volatile): ");
            String keyword = in.nextLine();

            Runnable enumerator = () -> {
                try {
                    enumerate(Path.of(directory));
                    queue.put(DUMMY);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                }
            };

            // 工作線程負責向隊列添加元素
            new Thread(enumerator).start();

            for (int i = 1; i <= SEARCH_THREADS; i++) {
                Runnable searcher = () -> {
                    try {
                        var done = false;
                        while (!done) {
                            Path file = queue.take();
                            if (file == DUMMY) {
                                queue.put(file);
                                done = true;
                            } else {
                                search(file, keyword);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                    }
                };
                // 工作線程負責從隊列移除元素
                new Thread(searcher).start();
            }
        }
    }

    /**
     * Recursively enumerates all files in a given directory and its subdirectories.
     * @param directory the directory in which to start
     */
    public static void enumerate(Path directory) throws IOException, InterruptedException {
        try (Stream<Path> children = Files.list(directory)) {
            for (Path child : children.collect(Collectors.toList())) {
                if (Files.isDirectory(child)) {
                    enumerate(child);
                } else {
                    queue.put(child);
                }
            }
        }
    }

    /**
     * Searches a file for a given keyword and prints all matching lines.
     *
     * @param file    the file to search
     * @param keyword the keyword to search for
     */
    public static void search(Path file, String keyword) throws IOException {
        try (var in = new Scanner(file, StandardCharsets.UTF_8)) {
            int lineNumber = 0;
            while (in.hasNextLine()) {
                lineNumber++;
                String line = in.nextLine();
                if (line.contains(keyword)) {
                    System.out.printf("%s:%d:%s%n", file, lineNumber, line);
                }
            }
        }
    }
}

上面程序通過阻塞隊列的方式搜索指定文件夾下的所有文件是否有某個關鍵字。
程序通過一批工作線程來完成:
一個生產(chǎn)者工作線程通過調(diào)用遞歸方法來遍歷文件夾舱污,并把文件路勁塞入阻塞隊列呀舔。
一批消費者工作線程通過從阻塞隊列中隊頭挨個取文件路徑,再根據(jù)文件路徑搜索指定文件是否含有某個關鍵字扩灯。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末媚赖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驴剔,更是在濱河造成了極大的恐慌省古,老刑警劉巖粥庄,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧失,死亡現(xiàn)場離奇詭異,居然都是意外死亡惜互,警方通過查閱死者的電腦和手機布讹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來训堆,“玉大人描验,你說我怎么就攤上這事】佑悖” “怎么了膘流?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲁沥。 經(jīng)常有香客問我呼股,道長,這世上最難降的妖魔是什么画恰? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任彭谁,我火速辦了婚禮,結果婚禮上允扇,老公的妹妹穿的比我還像新娘缠局。我一直安慰自己,他們只是感情好考润,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布狭园。 她就那樣靜靜地躺著,像睡著了一般糊治。 火紅的嫁衣襯著肌膚如雪唱矛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音揖赴,去河邊找鬼馆匿。 笑死,一個胖子當著我的面吹牛燥滑,可吹牛的內(nèi)容都是我干的渐北。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铭拧,長吁一口氣:“原來是場噩夢啊……” “哼赃蛛!你這毒婦竟也來了?” 一聲冷哼從身側響起搀菩,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呕臂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肪跋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歧蒋,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年州既,在試婚紗的時候發(fā)現(xiàn)自己被綠了谜洽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吴叶,死狀恐怖阐虚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚌卤,我是刑警寧澤实束,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逊彭,受9級特大地震影響咸灿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诫龙,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一析显、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧签赃,春花似錦谷异、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孔庭,卻和暖如春尺上,著一層夾襖步出監(jiān)牢的瞬間材蛛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工怎抛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卑吭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓马绝,卻偏偏與公主長得像豆赏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子富稻,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355