簡體   English   中英

如何實現可由多個線程處理的隊列?

[英]How to implement a queue that can be processed by multiple threads?

我想我做錯了。 我正在創建線程,假設從共享隊列中處理一些數據。 我的問題是程序很慢並且內存耗盡,我懷疑隊列可能不像我希望的那樣共享。 我懷疑這是因為在我的代碼中我添加了一行顯示隊列的大小,如果我啟動2個線程,那么我得到兩個輸出具有完全不同的數字並且似乎自己遞增(我認為它可能是相同的數字但是也許是從100跳到2,依此類推,但是在觀看它顯示105和5並且以不同的速率前進。如果我有4個線程,那么我看到4個不同的數字)。

這是相關部分的片段。 我在程序頂部的隊列中創建了一個包含我想要的數據的靜態類

static class queue_class {
        int number;
        int[] data;
        Context(int number,  int[] data) {
            this.number = number;
            this.data = data;
        }
    }

然后我將一些作業發送到callable后創建隊列..

static class process_threaded implements Callable<Void> {
    // queue with contexts to process
    private Queue<queue_class> queue;

    process_threaded(queue_class request) {
        queue = new ArrayDeque<queue_class>();
        queue.add(request);
    }

    public Void call() {
        while(!queue.isEmpty()) {
            System.out.println("in contexts queue with a size of " + queue.size());
            Context current = contexts.poll();
            //get work and process it, if it work great then the solution goes elsewhere
            //otherwise, depending on the data, its either discarded or parts of it is added back to queue
            queue.add(new queue_class(k, data_list)); 

正如您所看到的,數據有3個選項,如果數據良好則被發送,如果數據完全可怕則被丟棄或被發送回隊列。 我認為隊列在發送回來時會發生但我懷疑是因為每個線程都在自己的隊列中而不是共享隊列。

這個猜測是否正確,我做錯了嗎?

在評估中,每個線程(可能)使用自己的隊列是正確的,因為您在Callable的構造函數中創建了一個隊列。 (擁有Callable<Void>實際上非常奇怪 - 不僅僅是Runnable嗎?)

還有其他問題,例如,您正在使用不是線程安全的隊列,或者您的代碼在編寫時不會編譯的事實。

但是,重要的問題是,你真的需要首先明確地創建一個隊列嗎? 為什么沒有提交CallableExecutorService (如果您決定進行切換,則為Runnables ):將對執行程序的引用傳遞給Callable ,並且可以將新的Callable添加到執行程序的任務隊列中以運行。 無需重新發明輪子。

例如:

static class process_threaded implements Runnable {
    // Reference to an executor
    private final ExecutorService exec;
    // Reference to the job counter
    private final AtomicInteger jobCounter;
    // Request to process
    private queue_class request;

    process_threaded( ExecutorService exec, AtomicInteger counter, queue_class request) {
        this.exec = exec;
        this.jobCounter = counter;
        this.jobCounter.incrementAndGet(); // Assuming that you will always
                                           // submit the process_threaded to
                                           // the executor if you create it.
        this.request = request;
    }

    public run() {
        //get work and process **request**, if it work great then the solution goes elsewhere
        //otherwise, depending on the data, its either discarded or parts of are added back to the executor
        exec.submit( new process_threaded( exec, new queue_class(k, data_list) ) );

        // Can do some more work

        // Always run before returning: counter update and notify the launcher
        synchronized(jobCounter){
            jobCounter.decrementAndGet();
            jobCounter.notifyAll();
        }
    }
}

編輯:

為了解決何時關閉執行程序的問題,我認為最簡單的解決方案是使用作業計數器,並在達到0時關閉。對於線程安全, AtomicInteger可能是最佳選擇。 我在上面添加了一些代碼來合並更改。 然后你的啟動代碼看起來像這樣:

void theLauncher() {

    AtomicInteger jobCounter = new AtomicInteger( 0 );

    ExecutorService exec = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcesses());

    exec.submit( new process_threaded( exec, jobCounter, someProcessRequest ) );
    // Can submit some other things here of course...

    // Wait for jobs to complete:
    for(;;jobCounter.get() > 0){
        synchronized( jobCounter ){ // (I'm not sure if you have to have the synchronized block, but I think this is safer.
            if( jobCounter.get() > 0 )
                jobCounter.wait();
        }
    }

    // Now you can shutdown:
    exec.shutdown();
}

不要重新發明輪子! 如何使用ConcurrentLinkedQueue 來自javadocs:

基於鏈接節點的無界線程安全隊列。 此隊列對元素FIFO(先進先出)進行排序。 隊列的頭部是隊列中最長時間的元素。 隊列的尾部是隊列中最短時間的元素。 新元素插入隊列的尾部,隊列檢索操作獲取隊列頭部的元素。 當許多線程共享對公共集合的訪問時,ConcurrentLinkedQueue是一個合適的選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM