在执行另一个之前等待SwingWorker完成

时间:2017-08-11 12:20:22

标签: java multithreading swing concurrency swingworker

我试着在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()方法有何不同......我尝试在doInBackground()进行比较和打印但这会弄乱格式化(这是预期的 - 在打印完成之前,背景颜色会发生变化)。我也尝试在调用Thread.sleep()的循环中调用SwingWorker一段任意时间,如下所示:

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挂起,直到比较所有文件对。

有解决方案吗?

1 个答案:

答案 0 :(得分:1)

我完成OrderedResultsExecutors后,按照通知结果的顺序维护添加任务的顺序。您所要做的就是为您的案例实施notify方法,例如。写一些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>());
    }

}