簡體   English   中英

以線程安全方式使用ExecutorService和ProgressMonitor

[英]Using ExecutorService and ProgressMonitor in a Thread Safe Manner

我首先定義ProgressMonitor:

progressMonitor = new ProgressMonitor(parent, "Starting processing ...", "", 0, maxNumberProcesses+1);
progressMonitor.setProgress(0);

並在同一線程上使用ExecutorService和invokeAll()處理可調用對象列表:

ExecutorService execService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // use all available processors at startup
execService.invokeAll(callables); // wait for all tasks to complete
execService.shutdownNow(); // free thread pool resources

每個Callable的形式為:

class Callable implements Callable<List<String>>
{
    public List<String> call()
    {
        List<String> files = doSomeStuff();
        progressBarUpdate();
        return files;
    }
}

即; 每個Callable調用progressBarUpdate():

private void progressBarUpdate()
{
    if (progressMonitor != null)
    {
        Lock lock = new ReentrantLock();
        lock.lock();
        try
        {
            progressMonitor.increment();
        }
        finally
        {
            lock.unlock(); // release lock
        }
    }
}

每個doSomeStuff()都有其自己的異常處理,如果發生錯誤或引發異常,則返回null值。 這就是為什么返回類型為List的原因,在這種情況下返回null。 可調用對象與它們返回的文件列表之間沒有交叉,它們都維護着自己的文件列表。

我發現它工作正常,但偶爾會拋出以下形式的InterruptedException:

Disposal was interrupted:
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:503)
at java.awt.EventQueue.invokeAndWait(EventQueue.java:1263)
at java.awt.Window.doDispose(Window.java:1209)
at java.awt.Dialog.doDispose(Dialog.java:1196)
at java.awt.Window.dispose(Window.java:1147)
at javax.swing.ProgressMonitor.close(ProgressMonitor.java:311)
at javax.swing.ProgressMonitor.setProgress(ProgressMonitor.java:264)

顯示達到監視器最大值時setProgress()調用close():

public void setProgress(int nv) {
    if (nv >= max) {
        close();
    }
...

和close()包含許多其他非線程安全調用。

我已經修改了代碼,以使條件nv> = max不滿足,並且在invokeAll()之后顯式調用ProgressMonitor.close(),但我仍然不相信這種方法是完全線程安全的。

還有其他人遇到這種情況並找到了堅如磐石的解決方案嗎?

謝謝

格雷厄姆

PS。 請注意,ProgressMonitor不是Swing小部件,但確實封裝了Swing組件。 結果,我確保ProgressMonitor不在EDT上運行。

如果要執行后台任務並顯示進度,則應使用SwingWorker SwingWorker具有一個可以監聽的progress屬性。 它確保進度更新是在事件調度線程中完成的,而任務是在后台線程中完成的。

例如:

SwingWorker<?,?> task = ...;
final JProgressBar progressBar = new JProgressBar(0, 100);

task.addPropertyChangeListener(
        new PropertyChangeListener() {
            public  void propertyChange(PropertyChangeEvent evt) {
                 if ("progress".equals(evt.getPropertyName())) {
                     progressBar.setValue((Integer)evt.getNewValue());
            }
        }
 });

完整的示例代碼在SwingWorker的javadoc中。

看起來這是引起interrupt

Worker Thread (Callable1):
close() -> doDispose() -> EventQueue.invokeAndWait() {
synchronized (lock) {
            Toolkit.getEventQueue().postEvent(event);
            lock.wait(); // --> (2) blocked window disposal event gets interrupted
        }
}

ExecutorService.shutdownNow() :
 try {
                for (Worker w : workers) {
                    w.interruptNow(); // (1) --> Setting interrupt flag
                }
            } catch (SecurityException se) { // Try to back out
                runState = state;
                // tryTerminate() here would be a no-op
                throw se;
            }

從功能上來說,當進度監視器完成(或達到max )時,在分派事件之前(是否可以發生取決於eventQ的繁忙程度),服務將自行關閉。

從概念上講,我在您的代碼中看不到任何特定的問題,但是它主要是您的執行者服務與AWT.EventQueue隱式線程通信。

您可以在progressMonitor.increment();周圍使用InterruptedException progressMonitor.increment(); 或在調用shutdownNow之前使用ExecutorService.awaitTermination

暫無
暫無

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

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