我首先定义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每个可调用对象的形式如下:
class Callable implements Callable<List<String>>
{
public List<String> call()
{
List<String> files = doSomeStuff();
progressBarUpdate();
return files;
}
}即,每个可调用函数都调用progressBarUpdate():
private void progressBarUpdate()
{
if (progressMonitor != null)
{
Lock lock = new ReentrantLock();
lock.lock();
try
{
progressMonitor.increment();
}
finally
{
lock.unlock(); // release lock
}
}
}每个doSomeStuff()都有自己的异常处理,如果发生错误或抛出异常,则返回空值。这就是为什么返回类型是列表,并且在这种情况下返回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上运行。
发布于 2013-12-24 18:38:33
如果你想做一个后台任务并显示进度,你应该使用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中。
发布于 2013-12-24 18:38:56
看起来这就是导致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,也可以在调用shutdownNow之前使用ExecutorService.awaitTermination。
https://stackoverflow.com/questions/20759129
复制相似问题