Reputation: 771
I'm building a Java application and I need to keep track of how many seconds have past from the start of the program. I'm trying to achieve this with a thread, so I have a main class that creates the timeController
thread, waits 5 seconds and then stops it, for testing purposes the only thing that timeController
is supposed to do now is to print the seconds elapsed every 0,5 seconds. But keep in mind that my final goal is to have multiple threads of a different object asking to the timeController
the seconds elapsed in a synchronized manner. For now it only prints the first Calendar.SECOND
and never updates that value, since it runs for 5 seconds before interruption and updates every half a second, I would expect to see something like 44 44 45 45 46 46 47 47 48 48 (assuming when it started the systems seconds were 44). Thank you.
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.
}
}
}
}
Upvotes: 0
Views: 634
Reputation: 533500
You can replace your class with
public enum TimeController {
INSTANCE;
final long started = System.currentTimeMillis();
public int getElapsedSeconds() {
return (System.currentTimeMillis() - started) / 1000;
}
@Deprecated
public static TimeController getInstance() {
return INSTANCE;
}
}
or even simplier
public enum TimeController {
;
static final long started = System.currentTimeMillis();
public static int getElapsedSeconds() {
return (System.currentTimeMillis() - started) / 1000;
}
}
Upvotes: 0
Reputation: 771
Thank you for everyone that tried to help, while there is gonna be a synchronization problem in the future when multiple threads try to get the time from the TimeController, that is not the case for this particular instance of the problem. I found out that the problem was that I was keeping a static reference to a Calendar, when I really should be using Calendar.getInstance() inside the run method to update the reference on each run. Here is how it should look:
@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.
}
}
}
Upvotes: 0
Reputation: 4592
You should compute elapsed time at the instant you need it, every time you need, and only when you need it, in the same thread as your main code. There is no need for a separate thread constantly computing and storing an elapsed time value.
Upvotes: 1
Reputation: 10084
For now it only prints the first Calendar.SECOND and never updates that value
It seems that you want to provide a concurrent solution to your timing problem, but none of the code in your timing thread is synchronized. This block, at least, is suspect:
private Calendar calendarInstance = Calendar.getInstance();
private int secondsElapsed = 0;
private int incialSeconds = 0;
public int getElapsedSeconds(){
return secondsElapsed;
}
There is no synchronization going on here. Because threads may be executing on different cores with different local cache memory, there is no guarantee whatsoever that changes made to a variable in one thread will ever be visible to another thread executing on a different processor core. That's probably what's happening in your code.
You'll want to carefully define the state space of your mutable objects (here, TimerController) and implement a thread safety policy that makes sense for your use case. The goal of your policy is to guard the mutable state of your objects from race conditions that cause liveness and safety failures (i.e., to prevent invalid state transitions). A common way to address this is to use the monitor pattern to achieve mutual exclusion and visibility through the use of locks.
Upvotes: 2