1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
| package com.glaurent.utils;
/**
* The CountDown class implements a count down features.
* @author glaurent
*/
public class CountDown extends Thread {
/** Remaining time before the CountDown is over (in milliseconds). */
private long _delay;
/** Action to launch every second (round second). */
private CountDownAction _ticAction;
/** Action to launch when the CountDown reaches zero. */
private CountDownAction _lastAction;
/** Indicates if the CountDown is in pause mode. */
private boolean _pause = false;
/**
* Constructs a CountDown but do not start it.
* @param delay Duration (in seconds) of the CountDown to create.
* @param ticAction Action to launch every second (round second). ticAction should be as quick as possible.
* If it lasts more than a second, and the next tic occurs and the current ticAction is not yet terminated,
* the next ticAction won't be launched. ticAction can be set to null if no action should be launched every
* second.
* There is no guarantee that ticAction will be executed <u>every</u> second.
* Also, tickAction can be launched seeveral times for the same second.
* @param lastAction Action to launch when the CountDown reaches zero. lastAction cannot be set to null.
* @exception IllegalArgumentException Thrown if delay is negative or null or if lastAction is set to null.
*/
public CountDown(int delay, CountDownAction ticAction, CountDownAction lastAction) {
if(delay <= 0) {
throw new IllegalArgumentException("CountDown delay must be stricly positive (here set to " + _delay + "seconds)");
}
if(lastAction == null) {
throw new IllegalArgumentException("CountDown last action must be set (here set to null)");
}
_delay = delay * 1000;
_ticAction = ticAction;
_lastAction = lastAction;
}
/**
* Starts this CountDown. When this CountDown reaches zero, the last action specified when this CountDown
* was constructed will be launched.
*/
public void start() {
super.start();
}
/**
* Pause this CountDown. The CountDown will be resumed when the method unpause() is called.
* If this CountDown is already paused, nothing will occur.
* @see unpause()
*/
public void pause() {
synchronized(this) {
if(!_pause) {
_pause = true;
notify();
}
}
}
/**
* Unpause this CountDown that has been paused by a previous call of the pause() method.
* If this CountDown is not paused, nothing will occur.
* @see pause()
*/
public void unpause() {
synchronized(this) {
if(_pause) {
_pause = false;
notify();
}
}
}
/**
* Interrupts this CountDown. When called, this CountDown cannot be started again.
* To pause this CountDown, use the pause() method instead.
* @see start()
* @see pause()
*/
public void interrupt() {
super.interrupt();
}
/**
* Gets the remaining time (in seconds) before this CountDown reaches zero.
* @return The remaining time (in seconds) of this CountDown.
*/
public int getRemainingTime() {
return Math.round(_delay / 1000f);
}
/**
* Sets the remaining time (in seconds) of this CountDown.
* Setting the remaining time of an interrupted CountDown or a CountDown that have reached zero is useless
* since it cannot be started again.
* This method can be called on a unstarted, started or paused CountDown.
* @param delay The new time remaining time (in seconds) of this CountDown.
*/
public void setRemainingTime(int delay) {
_delay = delay * 1000;
}
/**
* Internal use only!
* This method shouldn't ever be called by an application.
*/
public void run() {
synchronized(this) {
try {
final CountDown cd = this;
Runnable tic = new Runnable() {
public void run() {
synchronized(_ticAction) {
if(isAlive() && !isInterrupted()) {
_ticAction.doAction(cd);
}
}
}
};
long startTime, sleepTime;
Thread ticThread = null;
while(_delay > 0) {
// Count even the "while(_pause)" condition execution time
startTime = System.nanoTime();
// Pauses the thread if in pause mode
while(_pause) {
wait();
// Possible end of pause, reset startTime
startTime = System.nanoTime();
}
sleepTime = _delay % 1000l;
wait((sleepTime == 0l ? 1000l : sleepTime));
// Launch the tic action
if(_ticAction != null && (ticThread == null || !ticThread.isAlive())) {
ticThread = new Thread(tic);
ticThread.start();
}
_delay -= (System.nanoTime() - startTime) / 1000000l;
}
synchronized(_lastAction) {
// Stop the tic action if exists
if(_ticAction != null && ticThread != null) ticThread.interrupt();
// Launch the last action
_lastAction.doAction(this);
}
} catch(InterruptedException ie) {
// Do nothing but simply ends the run method without executing _lastAction
}
}
}
/**
* Unit tests.
* @param args Unused.
*/
public static void main(String[] args) throws Exception {
CountDown count;
CountDownAction tic, last;
System.out.println("Constructing tic action...");
tic = new CountDownAction() {
public void doAction(CountDown cd) {
System.out.println("Tic (remaining " + cd.getRemainingTime() + " secs)");
}
};
System.out.println("Tic action constructed");
System.out.println("Constructing last action...");
last = new CountDownAction() {
public void doAction(CountDown cd) {
System.out.println("Time's up (remaining " + cd.getRemainingTime() + " secs)");
}
};
System.out.println("Last action constructed");
System.out.println("Constructing count down...");
count = new CountDown(10, tic, last);
System.out.println("Count down constructed");
System.out.println("Starting count down...");
count.start();
System.out.println("Count down started");
Thread.sleep(5500);
System.out.println("Pausing count down... (" + count.getRemainingTime() + " seconds remaining)");
count.pause();
System.out.println("Count down paused (" + count.getRemainingTime() + " seconds remaining)");
Thread.sleep(4000);
System.out.println("Pausing count down (again)... (" + count.getRemainingTime() + " seconds remaining)");
count.pause();
System.out.println("Count down paused (again) (" + count.getRemainingTime() + " seconds remaining)");
Thread.sleep(4000);
System.out.println("Unpausing count down... (" + count.getRemainingTime() + " seconds remaining)");
count.unpause();
System.out.println("Count down unpaused (" + count.getRemainingTime() + " seconds remaining)");
}
} |
Partager