0

我正在嘗試使用線程在java中執行長操作(爲一批大文件生成md5sums)時在CLI中顯示進度條。在命令行上更新線程進度

我寫了一些代碼,它的工作原理,但我想知道我是否正確使用線程,因爲我很新。

我有兩個類文件,ProgressThreadTest.javaCountToABillion.java

CountToABillion.java:

public class CountToABillion implements Runnable { 

    double count = 0; 

    public void echoCount() { 
     System.out.println(count); 
    } 

    @Override 
    public void run() {  
     for (double x=0;x<1000000000;x++) { 
      try { 
       Thread.sleep(10); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      count = x; 
      echoCount(); 
     }  
    } 
} 

ProgressThreadTest.java:

public class ProgressThreadTest { 

    public static void main(String[] args) throws InterruptedException { 

     Thread doCount=new Thread(new CountToABillion()); 
     doCount.start(); 
    } 
} 

它可以在命令行上如預期向上計數。

任何人有任何意見,這是否是一個很好的方式來做線程?

此外,因爲我正在更新計數循環中的進度,它將每隔10ms更新一次。我如何將代碼更改爲每秒只輸出一次計數?

+0

,對於一個月循環將循環工作。 –

+0

爲什麼您需要線程來更新CLI上的進度? –

+0

您可以使用[Timer](http://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html)或[ScheduledThreadPoolExecutor](http://docs.oracle.com/javase /6/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html)定期打印您的進度。 –

回答

2

使用javax.swing.Timer可能是簡單的解決方案:

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.Timer; 

public class CountToABillion implements Runnable { 

    double count = 0; 
    Timer progressTimer; 

    public void echoCount() { 
     System.out.println(count); 
    } 

    @Override 
    public void run() { 
     progressTimer = new Timer(1000, new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       echoCount(); 
      } 
     }); 
     progressTimer.setRepeats(true); 
     progressTimer.start(); 

     for (double x=0;x<1000000000;x++) { 
      try { 
       Thread.sleep(10); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      count = x; 
     } 
     progressTimer.stop(); 
    } 
} 

使用java.util.concurrent.ScheduledThreadPoolExecutor是更好的,更靈活的解決方案:

import java.util.concurrent.ScheduledThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 

public class CountToABillion implements Runnable { 

    double count = 0; 
    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1); 

    public void echoCount() { 
     System.out.println(count); 
    } 

    @Override 
    public void run() { 
     Runnable task = new Runnable() { 
      public void run() { 
       echoCount(); 
      } 
     }; 
     exec.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS); 

     for (double x=0;x<1000000000;x++) { 
      try { 
       Thread.sleep(10); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      count = x; 
     } 
     exec.shutdownNow(); 
    } 
} 
+0

完美!我不太瞭解'ScheduledThreadPoolExecutor',但我會學習它。非常感謝! – localhost

1
@Override 
public void run() {  
    for (int i = 0; i < 1000000000; i++) { 
     try { 
      Thread.sleep(10); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     count = i; 

     if(i % 100 == 0) { 
      echoCount(); 
     } 
    } 
} 

使用i % 100 == 0你檢查是否i是整除100沒有休息值。如果是這樣的話,那就意味着你跑了100次10ms,這是1000ms,這是1s。所以你會每秒輸出你的echoCountt()

這是使用線程的好方法。但是你的線程會一直運行,直到它達到10000000秒。

在旁註中,您應該在for循環中使用'i'而不是'x'。它更廣泛地使用這樣的,將是更易於讀取,經驗豐富的Java開發人員

+0

是的,我知道大聲笑,但這是OP的問題,不是我的^^我在回答中提到他的線程將持續很長時間的循環。也許這就是他想要的?我不知道。 – JREN

+0

:D加載屏幕將永遠^^ –

+0

我認爲OP想要每10ms繼續增加計數器(或實際執行未知持續時間的操作),但僅顯示每1秒的輸出。 –

1

選項1:

int sleep = 10; //ms 
    int echo = 1000; //ms 
    for (double x=0;x<1000000000;x++) { 
     try { 
      Thread.sleep(sleep); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     count = x; 
     if((x*sleep % echo) == 0) { 
      echoCount(); 
     } 
    } 

選項2:

  • 創建一個新類來管理您的櫃檯,它應該可以添加,重置等等。你將不得不確保它是線程安全的(如果你想從不同的線程中更新,寫入新的值)。
  • 使一個線程在給定的時間間隔內增加計數器
  • 使另一個線程輪詢其他給定時間間隔內的當前計數並打印。
1
long last=0; 
@Override 
public void run() {  
    for (double x=0;x<1000000000;x++) { 
     try { 
      doWork(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     count = x; 
     if((System.currentTimeMillis()-last)>=1000) //post every second 
     { 
      last=System.currentTimeMillis(); 
      echoCount(); 
     } 
    }  
} 

這將打印計數每秒一次假設「工作」並不需要超過一秒鐘。

+0

問題是,每個md5sum代需要20秒,因此我認爲我需要線程。也許我的計數例子不好,我應該只用一個需要20s的單個任務。 – localhost

+0

是的,所以你需要有一個線程檢查隊列以查看它的大小(並且每秒打印出結果),然後是2xcpu線程,它從同步隊列中拉出工作,處理它並將其處理-隊列。 –