在执行另一个 SwingWorker 之前等待 SwingWorker 完成
Waiting for a SwingWorker to finish before executing another
我试图在 SO 上找到我的问题的答案,但由于它们的丰富性和多样性,我有点困惑。这是我的问题:我的应用比较两个文件并在 Swing.JTextPane
中打印出结果。我用按钮调用处理文件的代码,为了避免挂起 UI,我用 SwingWorker
处理每对文件。这是它的代码:
class ProcessAndPrintTask extends SwingWorker<Void, Void> {
private Report report;
Integer reportResult;
ProcessAndPrintTask(Report report) {
this.report = report;
reportResult = null;
}
@Override
protected Void doInBackground() {
try {
reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())));
}
catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
@Override
protected void done() {
String message = report.getFilename() + ": ";
if (reportResult != null) {
switch (reportResult) {
case 1:
StyleConstants.setBackground(style, Color.GREEN);
try {
doc.insertString(doc.getLength(), message + "MATCH\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
case 0:
StyleConstants.setBackground(style, Color.RED);
try {
doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style);
try {
for (String s : report.getComparator().getDifferences(
new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())))) {
doc.insertString(doc.getLength(), s + "\n", style);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
case -1:
StyleConstants.setBackground(style, Color.CYAN);
try {
doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
default:
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "PROBLEM\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
}
}
else {
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
}
}
}
doInBackground()
进行比较,done()
根据比较结果格式化消息并打印。问题是程序不会等到一对被处理并打印出来,所以结果不会按照打开的顺序打印,这可能会让用户非常困惑:大多数文件都很小,而且真的过去了很快,因此比较似乎在某个时候完成,但仍有更大的文件正在处理。
我读到过使用 PropertyChangeListener
的可能性,但我看不出它与使用 done()
方法有何不同...我尝试在 [=14 中进行比较和打印=] 但这会弄乱格式(这是可以预料的 - 在打印完成之前,背景颜色会更改)。我还尝试在调用 SwingWorker
的循环内调用 Thread.sleep()
任意时间,看起来像这样:
try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) {
Workbook workbook = new XSSFWorkbook(reportListExcelFile);
Sheet sheet = workbook.getSheetAt(0);
Iterator<Row> iter = sheet.iterator();
// skip first row that contains columns names
iter.next();
while (iter.hasNext()) {
try {Thread.sleep(1000);} catch (Exception ex) {ex.printStackTrace();}
Row r = iter.next();
String name = r.getCell(0).getStringCellValue();
String format = r.getCell(1).getStringCellValue();
Report currentReport = new Report(name, format);
new ProcessAndPrintTask(currentReport).execute();
}
}
它不仅看起来是个丑陋的拐杖,而且导致 GUI 挂起,直到比较完所有文件对。
有解决办法吗?
一旦我完成了 OrderedResultsExecutors
维护添加任务的顺序和通知结果的顺序。您所要做的就是为您的案例实施通知方法,例如。写一些 Listener
什么的。当然,您可以将 Report 的集合传递给 SwingWorker 并在 for
循环中处理它们,但在这种情况下,您将失去多线程,并且所有任务在这种单线程中可能需要花费更多的时间来执行方式。这就是为什么最好使用这种机制的多线程版本,如下所示:
Import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class OrderedResultsExecutors extends ThreadPoolExecutor {
public OrderedResultsExecutors(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
private ConcurrentHashMap<Long, Runnable> startedTasks = new ConcurrentHashMap<>();
private ConcurrentLinkedDeque<Runnable> finishedTasks = new ConcurrentLinkedDeque<>();
private AtomicLong toNotify = new AtomicLong(0);
private AtomicLong submitedCount = new AtomicLong(0);
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
startedTasks.put(submitedCount.getAndIncrement(), r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
finishedTasks.add(r);
finishedTask();
}
private void finishedTask() {
Runnable orderedResult;
long current;
while ((orderedResult = startedTasks.get(current = toNotify.get())) != null
&& finishedTasks.contains(orderedResult) && (orderedResult = startedTasks.remove(current)) != null) {
finishedTasks.remove(orderedResult);
notify(current, orderedResult);
toNotify.incrementAndGet();
}
}
private void notify(long order, Runnable result) {
try {
System.out.println("order: " + order + " result: " + ((Future)result).get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public static ExecutorService newFixedThreadPool(int noOfThreads) {
int corePoolSize = noOfThreads;
int maximumPoolSize = noOfThreads;
return new OrderedResultsExecutors(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
}
答案(由 Sergiy Medvynskyy 建议)是摆脱在循环中调用的 "infinitely many" SwingWorkers
,只制作一个使用项目列表来处理和打印并执行在 doInBackground()
.
中循环
重构后的代码如下所示:
class ProcessAndPrintTask extends SwingWorker<Void, Void> {
private List<Report> reports;
Integer reportResult;
ProcessAndPrintTask(List<Report> reports) {
this.reports = reports;
}
@Override
protected Void doInBackground() {
for (Report report : reports) {
try {
reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())));
} catch (IOException ex) {
ex.printStackTrace();
}
String message = report.getFilename() + ": ";
if (reportResult != null) {
switch (reportResult) {
case 1:
StyleConstants.setBackground(style, Color.GREEN);
try {
doc.insertString(doc.getLength(), message + "MATCH\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
case 0:
StyleConstants.setBackground(style, Color.RED);
try {
doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style);
try {
for (String s : report.getComparator().getDifferences(
new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())))) {
doc.insertString(doc.getLength(), s + "\n", style);
}
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
case -1:
StyleConstants.setBackground(style, Color.CYAN);
try {
doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
default:
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "PROBLEM\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
else {
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style);
}
catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
return null;
}
}
这里我称 SwingWorker.execute()
:
try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) {
Workbook workbook = new XSSFWorkbook(reportListExcelFile);
Sheet sheet = workbook.getSheetAt(0);
Iterator<Row> iter = sheet.iterator();
java.util.List<Report> reports = new ArrayList<>();
// skip first row that contains columns names
iter.next();
while (iter.hasNext()) {
Row r = iter.next();
String name = r.getCell(0).getStringCellValue();
String format = r.getCell(1).getStringCellValue();
Report currentReport = new Report(name, format);
reports.add(currentReport);
}
new ProcessAndPrintTask(reports).execute();
}
它不是很漂亮,但是很管用:)
我试图在 SO 上找到我的问题的答案,但由于它们的丰富性和多样性,我有点困惑。这是我的问题:我的应用比较两个文件并在 Swing.JTextPane
中打印出结果。我用按钮调用处理文件的代码,为了避免挂起 UI,我用 SwingWorker
处理每对文件。这是它的代码:
class ProcessAndPrintTask extends SwingWorker<Void, Void> {
private Report report;
Integer reportResult;
ProcessAndPrintTask(Report report) {
this.report = report;
reportResult = null;
}
@Override
protected Void doInBackground() {
try {
reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())));
}
catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
@Override
protected void done() {
String message = report.getFilename() + ": ";
if (reportResult != null) {
switch (reportResult) {
case 1:
StyleConstants.setBackground(style, Color.GREEN);
try {
doc.insertString(doc.getLength(), message + "MATCH\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
case 0:
StyleConstants.setBackground(style, Color.RED);
try {
doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style);
try {
for (String s : report.getComparator().getDifferences(
new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())))) {
doc.insertString(doc.getLength(), s + "\n", style);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
case -1:
StyleConstants.setBackground(style, Color.CYAN);
try {
doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
break;
default:
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "PROBLEM\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
}
}
else {
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style);
}
catch (BadLocationException ex) {ex.printStackTrace();}
}
}
}
doInBackground()
进行比较,done()
根据比较结果格式化消息并打印。问题是程序不会等到一对被处理并打印出来,所以结果不会按照打开的顺序打印,这可能会让用户非常困惑:大多数文件都很小,而且真的过去了很快,因此比较似乎在某个时候完成,但仍有更大的文件正在处理。
我读到过使用 PropertyChangeListener
的可能性,但我看不出它与使用 done()
方法有何不同...我尝试在 [=14 中进行比较和打印=] 但这会弄乱格式(这是可以预料的 - 在打印完成之前,背景颜色会更改)。我还尝试在调用 SwingWorker
的循环内调用 Thread.sleep()
任意时间,看起来像这样:
try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) {
Workbook workbook = new XSSFWorkbook(reportListExcelFile);
Sheet sheet = workbook.getSheetAt(0);
Iterator<Row> iter = sheet.iterator();
// skip first row that contains columns names
iter.next();
while (iter.hasNext()) {
try {Thread.sleep(1000);} catch (Exception ex) {ex.printStackTrace();}
Row r = iter.next();
String name = r.getCell(0).getStringCellValue();
String format = r.getCell(1).getStringCellValue();
Report currentReport = new Report(name, format);
new ProcessAndPrintTask(currentReport).execute();
}
}
它不仅看起来是个丑陋的拐杖,而且导致 GUI 挂起,直到比较完所有文件对。
有解决办法吗?
一旦我完成了 OrderedResultsExecutors
维护添加任务的顺序和通知结果的顺序。您所要做的就是为您的案例实施通知方法,例如。写一些 Listener
什么的。当然,您可以将 Report 的集合传递给 SwingWorker 并在 for
循环中处理它们,但在这种情况下,您将失去多线程,并且所有任务在这种单线程中可能需要花费更多的时间来执行方式。这就是为什么最好使用这种机制的多线程版本,如下所示:
Import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class OrderedResultsExecutors extends ThreadPoolExecutor {
public OrderedResultsExecutors(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
private ConcurrentHashMap<Long, Runnable> startedTasks = new ConcurrentHashMap<>();
private ConcurrentLinkedDeque<Runnable> finishedTasks = new ConcurrentLinkedDeque<>();
private AtomicLong toNotify = new AtomicLong(0);
private AtomicLong submitedCount = new AtomicLong(0);
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
startedTasks.put(submitedCount.getAndIncrement(), r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
finishedTasks.add(r);
finishedTask();
}
private void finishedTask() {
Runnable orderedResult;
long current;
while ((orderedResult = startedTasks.get(current = toNotify.get())) != null
&& finishedTasks.contains(orderedResult) && (orderedResult = startedTasks.remove(current)) != null) {
finishedTasks.remove(orderedResult);
notify(current, orderedResult);
toNotify.incrementAndGet();
}
}
private void notify(long order, Runnable result) {
try {
System.out.println("order: " + order + " result: " + ((Future)result).get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public static ExecutorService newFixedThreadPool(int noOfThreads) {
int corePoolSize = noOfThreads;
int maximumPoolSize = noOfThreads;
return new OrderedResultsExecutors(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
}
答案(由 Sergiy Medvynskyy 建议)是摆脱在循环中调用的 "infinitely many" SwingWorkers
,只制作一个使用项目列表来处理和打印并执行在 doInBackground()
.
重构后的代码如下所示:
class ProcessAndPrintTask extends SwingWorker<Void, Void> {
private List<Report> reports;
Integer reportResult;
ProcessAndPrintTask(List<Report> reports) {
this.reports = reports;
}
@Override
protected Void doInBackground() {
for (Report report : reports) {
try {
reportResult = report.getComparator().compareTwoFiles(new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())));
} catch (IOException ex) {
ex.printStackTrace();
}
String message = report.getFilename() + ": ";
if (reportResult != null) {
switch (reportResult) {
case 1:
StyleConstants.setBackground(style, Color.GREEN);
try {
doc.insertString(doc.getLength(), message + "MATCH\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
case 0:
StyleConstants.setBackground(style, Color.RED);
try {
doc.insertString(doc.getLength(), message + "NO MATCH\n\n", style);
try {
for (String s : report.getComparator().getDifferences(
new FileInputStream(new File(pathToReportsA + report.getFilename())),
new FileInputStream(new File(pathToReportsB + report.getFilename())))) {
doc.insertString(doc.getLength(), s + "\n", style);
}
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
case -1:
StyleConstants.setBackground(style, Color.CYAN);
try {
doc.insertString(doc.getLength(), message + "BOTH FILES EMPTY\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
break;
default:
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "PROBLEM\n", style);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
else {
StyleConstants.setBackground(style, Color.ORANGE);
try {
doc.insertString(doc.getLength(), message + "FILE OR FILES NOT FOUND\n", style);
}
catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
return null;
}
}
这里我称 SwingWorker.execute()
:
try (FileInputStream reportListExcelFile = new FileInputStream(new File(reportListPath))) {
Workbook workbook = new XSSFWorkbook(reportListExcelFile);
Sheet sheet = workbook.getSheetAt(0);
Iterator<Row> iter = sheet.iterator();
java.util.List<Report> reports = new ArrayList<>();
// skip first row that contains columns names
iter.next();
while (iter.hasNext()) {
Row r = iter.next();
String name = r.getCell(0).getStringCellValue();
String format = r.getCell(1).getStringCellValue();
Report currentReport = new Report(name, format);
reports.add(currentReport);
}
new ProcessAndPrintTask(reports).execute();
}
它不是很漂亮,但是很管用:)