2017-08-11 151 views
0

我试图在这里找到我的问题的答案,但由于它们的丰富性和多样性,我有点困惑。这是我的问题:我的应用程序比较两个文件,并在Swing.JTextPane中输出结果。我称之为使用按钮处理文件的代码,并且为了避免挂起UI,我使用SwingWorker处理每对文件。下面是它的代码:在执行另一个之前等待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

你只需要启动第一个'done'方法中的第二个'SwingWorker'也可以只使用一个'SwingWorker',并在一个'doInBackground'方法中启动这两个任务。 –

+0

但是,我不得不将'Report'的集合传递给'SwingWorker',并且在worker中进行迭代,这是否正确? – DCzo

+0

试过了,完美无缺 - 谢谢! – DCzo

回答

1

一旦我完成了一个OrderedResultsExecutors,它按照通知结果的顺序维护添加任务的顺序。您所要做的就是为您的案例实施通知方法,例如。写一些Listener什么的。当然,您可以将一组报告传递给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>()); 
    } 

} 
+0

谢谢,但我希望能有更高层次的解决方案,这是Serqiy Medvynskyy在他的评论中提出的建议。 – DCzo

+0

如果你真的不需要这些任务的并行执行(也就是说他们都需要很短的时间),那么@SergiyMedvynskyy提供的灵魂确实很好,很简单。 –

+0

这是真的 - 并行执行并不是真的那么必要。对我来说重要的一点是保持gui的响应。不管怎样,谢谢你 :)。 – DCzo

0

答案(由Sergiy Medvynskyy建议)是摆脱了“无限多” 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(); 
      } 

这不是很漂亮,但它的工作原理:)

相关问题