2017-05-27 90 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; 
    } 
}