import java.lang.Thread.State;
import java.util.HashSet;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
public final class CountDown {
public enum EventType {
START,
STOP,
PAUSE,
UPDATE,
}
private final Object stateChangeSync = new Object();
private final Observable dispatcher = new Observable() {
@Override
public void notifyObservers(Object obj) {
setChanged();
super.notifyObservers(obj);
}
};
private final Set<Thread> waitingTasks = new HashSet<>();
private final long timeOut;
private long startTime, lastTime;
private Thread runner;
private boolean pause;
public CountDown(long timeOut) {
if (timeOut <= 0) {
throw new IllegalArgumentException("timeOut has tobe larger than 0");
}
this.timeOut = timeOut;
}
public void reset() {
startTime = lastTime = System.currentTimeMillis();
}
public void pause() {
pause(!pause);
}
public void pause(boolean set) {
synchronized (stateChangeSync) {
if (runner != null) {
synchronized (runner) {
if (set && runner.getState() != State.WAITING) {
try {
runner.wait();
} catch (InterruptedException e) {
// ignore
}
} else if (!set && runner.getState() == State.WAITING) {
reset();
runner.notify();
}
}
}
if(pause != set) {
dispatcher.notifyObservers(EventType.PAUSE);
}
pause = set;
}
}
public void start() {
synchronized (stateChangeSync) {
if (runner == null) {
runner = new Thread("Countdown") {
private long time;
@Override
public void run() {
while (!isInterrupted() && lastTime - startTime <= timeOut) {
time = System.currentTimeMillis();
if(time != lastTime) {
lastTime = time;
dispatcher.notifyObservers(EventType.UPDATE);
}
}
runner = null;
notifyWaiting();
dispatcher.notifyObservers(EventType.STOP);
}
};
reset();
pause(false);
runner.start();
dispatcher.notifyObservers(EventType.START);
}
}
}
public void stop() {
synchronized (stateChangeSync) {
if (runner != null) {
synchronized (runner) {
runner.notify();
runner.interrupt();
try {
runner.join();
} catch (InterruptedException e) {
// ignore
}
runner = null;
notifyWaiting();
dispatcher.notifyObservers(EventType.STOP);
}
}
}
}
public void join() {
Thread t = Thread.currentThread();
if(waitingTasks.add(t)) {
synchronized (t) {
try {
t.wait();
} catch(InterruptedException e) {
// ignore
}
}
}
}
public void addObserver(Observer obs) {
dispatcher.addObserver(obs);
}
public void removeObserver(Observer obs) {
dispatcher.deleteObserver(obs);
}
@Override
public String toString() {
long days = lastTime - startTime;
days = timeOut - days;
double tmp = days / 86400000.0;
days = (long) tmp;
tmp -= days;
tmp *= 24;
long hours = (long) tmp;
tmp -= hours;
tmp *= 60.0;
long minutes = (long) tmp;
tmp -= minutes;
tmp *= 60.0;
long seconds = (long) tmp;
tmp -= seconds;
long millis = (long) (tmp * 1000.0);
return String.format("%d days, %02d:%02d:%02d:%03d", days, hours, minutes, seconds, millis);
}
private void notifyWaiting() {
for(Thread t : waitingTasks) {
synchronized (t) {
t.notify();
}
}
}
public static void main(String[] args) {
final CountDown cd = new CountDown(60000);
cd.addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
if(arg instanceof EventType) {
switch((EventType) arg) {
case PAUSE:
break;
case START:
break;
case STOP:
break;
case UPDATE:
System.out.println(cd);
break;
}
}
}
});
cd.start();
cd.join();
}
}