2017-05-27 87 views
0

我正在構建一個Java應用程序,並且需要記錄程序啓動後已經過去多少秒。我試圖用一個線程實現這一點,所以我有一個創建timeController線程的主類,等待5秒鐘然後停止它,出於測試目的,timeController現在應該做的唯一事情是打印經過的秒數每0.5秒。但請記住,我的最終目標是讓一個不同對象的多個線程以同步的方式詢問以秒爲單位的時間。目前它只打印第一個Calendar.SECOND並且從不更新該值,因爲它在中斷前運行5秒並每半秒更新一次,所以我期望看到類似於44 44 45 45 46 46 47 47 48 48(假設它開始系統秒數是44)。謝謝。如何跟蹤秒數?

public class Main { 

    public static void main(String[] args) { 

     TimeController timeCon = TimeController.getInstance(); 

     timeCon.start(); 
     try { 
      Thread.sleep(5000); 
      timeCon.stop(); 
     } catch (Exception e){ 
      //ignore for now 
     } 
    } 
} 

public class TimeController implements Runnable{ 

    private static TimeController instance = null; 
    private volatile Thread timeCon; 

    private Calendar calendarInstance = Calendar.getInstance(); 
    private int secondsElapsed = 0; 
    private int incialSeconds = 0; 

    public int getElapsedSeconds(){ 
     return secondsElapsed; 
    } 

    public static TimeController getInstance(){ 
     if(instance == null){ 
      instance = new TimeController(); 
     } 
     return instance; 
    } 

    public void start(){ 
     timeCon = new Thread(this); 
     timeCon.start(); 
    } 

    public void stop(){ 
     timeCon = null; 
    } 

    @Override 
     public void run() { 
      Thread thisThread = Thread.currentThread(); 
      while (thisThread == timeCon) { 
       try { 
        Thread.sleep(500); 
        secondsElapsed = calendarInstance.get(Calendar.SECOND) - incialSeconds; 
        System.out.println(getElapsedSeconds()); 
       } catch (Exception e){ 
        //Ignore for now. 
       }    
      } 
     } 
} 
+1

其中一個關鍵的事情,瞭解多線程是更換類; **只有在有意義時才使用多線程**。即當你不能用一個線程來完成。否則,你只會增加複雜性和錯誤。 –

回答

2

現在只打印第一Calendar.SECOND和永遠不會更新 那些重視

看來,要提供給您的計時問題並行解決方案,但沒有的您的計時線程中的代碼已同步。至少這個塊是可疑的:

private Calendar calendarInstance = Calendar.getInstance(); 
private int secondsElapsed = 0; 
private int incialSeconds = 0; 

public int getElapsedSeconds(){ 
    return secondsElapsed; 
} 

這裏沒有進行同步。因爲線程可能在具有不同本地高速緩存存儲器的不同核心上執行,所以不能保證在一個線程中對變量所做的更改對於在另一個處理器核心上執行的另一個線程永遠不可見。這可能是你的代碼中發生的事情。

您需要仔細定義可變對象(此處爲TimerController)的狀態空間,並實現對您的用例有意義的線程安全策略。您的策略的目標是防止您的對象的可變狀態導致活躍性和安全性失敗(即阻止無效狀態轉換)。解決這個問題的常用方法是使用監視器模式通過使用鎖來實現互斥和可見性。

+0

感謝您的回覆,我對多線程新手仍在嘗試瞭解所有工作原理。那麼你會如何解決我的問題?我將如何去同步它? –

+0

我明白你的觀點,但出於測試的目的,即使我根本不使用任何額外的方法,只是在內部運行編碼,結果也是一樣的。 –

1

您應該計算經過時間在每次你需要你需要它的瞬間,只有當你需要的時候,在同一個線程中主代碼。不需要單獨的線程不斷計算和存儲經過的時間值。

0

非常感謝所有嘗試提供幫助的人,而未來當多線程嘗試從TimeController獲取時間時,會出現同步問題,而不是此問題的特定實例。我發現問題在於我保持對Calendar的靜態引用,當我真的應該在Run方法內使用Calendar.getInstance()來更新每次運行時的引用。下面是它應該如何看:

@Override 
public void run() { 
    Thread thisThread = Thread.currentThread(); 
    while (thisThread == timeCon) { 
     try { 
      Thread.sleep(500); 
      secondsElapsed = Calendar.getInstance().get(Calendar.SECOND) - incialSeconds; 
     } catch (Exception e){ 
      //Ignore for now. 
     } 
    } 
} 
0

您可以

public enum TimeController { 
    INSTANCE; 

    final long started = System.currentTimeMillis(); 

    public int getElapsedSeconds() { 
     return (System.currentTimeMillis() - started)/1000; 
    } 

    @Deprecated 
    public static TimeController getInstance() { 
     return INSTANCE; 
    } 
} 

甚至simplier

public enum TimeController { 
    ; 

    static final long started = System.currentTimeMillis(); 

    public static int getElapsedSeconds() { 
     return (System.currentTimeMillis() - started)/1000; 
    } 
}