我正在构建一个Java应用程序,我需要跟踪从程序开始经过的秒数。我试图通过一个线程实现这一点,所以我有一个创建timeController
线程的主类,等待5秒然后停止它,出于测试目的timeController
唯一的问题是应该现在做的是打印每0.5秒经过的秒数。但请记住,我的最终目标是让不同对象的多个线程以同步方式询问timeController
经过的秒数。现在它只打印第一个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.
}
}
}
}
答案 0 :(得分:2)
现在它只打印第一个Calendar.SECOND并且永远不会更新 那个价值
您似乎希望为计时问题提供并发解决方案,但计时线程中的所有代码都不会同步。至少,这个区块是可疑的:
private Calendar calendarInstance = Calendar.getInstance();
private int secondsElapsed = 0;
private int incialSeconds = 0;
public int getElapsedSeconds(){
return secondsElapsed;
}
这里没有同步。因为线程可能在具有不同本地高速缓存存储器的不同核上执行,所以无法保证对一个线程中的变量所做的更改将对在另一个处理器核上执行的另一个线程可见。这可能是你的代码中发生的事情。
您需要仔细定义可变对象的状态空间(此处为TimerController),并实现对您的用例有意义的线程安全策略。您的策略的目标是保护对象的可变状态免受导致活动和安全失败的竞争条件(即,防止无效状态转换)。解决此问题的常用方法是使用监视器模式通过使用锁来实现互斥和可见性。
答案 1 :(得分:1)
您应该计算所需的时间,每次,只有当需要时,与主代码相同的线程。不需要单独的线程不断地计算和存储经过的时间值。
答案 2 :(得分: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.
}
}
}
答案 3 :(得分: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;
}
}
甚至更简单
public enum TimeController {
;
static final long started = System.currentTimeMillis();
public static int getElapsedSeconds() {
return (System.currentTimeMillis() - started) / 1000;
}
}