2017-08-11 148 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(); 
      } 

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

相關問題