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 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
| package fr.brouillard.lang;
/**
* ThreadTemplate ease the use of threads that need to be stop/pause/resume.
*
* @author McFoggy aka Matthieu BROUILLARD (brouillard[DOT]matthieu[AT]gmail.com) : http://blog.matthieu.brouillard.fr
*/
public abstract class ThreadTemplate {
/**
* Default time used to wait on each execution loop
*/
public static int DEFAULT_LOOP_WAIT = 50;
private boolean executeOnlyOnce;
private int loopWaitTime;
private boolean mustWait;
private Thread t;
private boolean threadSuspended;
private Object waiter = new Object(); // private object used for synchronisation purposes to pause/resume the inner thread
/**
* Default constructor, will build a ThreadTemplate that will run an execution loop without waiting between two loops and that will start immediately after the call to {@link #start()}
*/
public ThreadTemplate() {
this(false, false, DEFAULT_LOOP_WAIT, false);
}
/**
* Constructor allowing to choose a single execution only if the given parameter is true.
* The execution will start once the {@link #start()} method is called. If the given parameter is false then the ThreadTemplate will run an execution loop without waiting between two loops.
* @param executeOnce true to execute the {@link #doIt()} method only once, false to execute it several times
*/
public ThreadTemplate(boolean executeOnce) {
this(executeOnce, false, DEFAULT_LOOP_WAIT, false);
}
/**
* Builds a ThreadTemplate object allowing to choose the waiting mode in the execution loop.
* @param waitOnLoop true to wait between two loops execution
* @param waitTime time in milliseconds to wait between two loops execution, will only be used if waitOnLoop is true
*/
public ThreadTemplate(boolean waitOnLoop, int waitTime) {
this(false, waitOnLoop, waitTime, false);
}
/**
* Constructor allowing to choose the loop mode and the suspend state of the ThreadTemplate
* @param executeOnce true to execute the {@link #doIt()} method only once, false to execute it several times
* @param startSuspended if true then the execution will be paused once the {@link #start()} method is called until a call to the {@link #resume()} method
*/
public ThreadTemplate(boolean executeOnce, boolean startSuspended) {
this(executeOnce, false, DEFAULT_LOOP_WAIT, startSuspended);
}
/**
* Constructor defining the wait mode and the suspend state of the ThreadTemplate
* @param waitOnLoop true to wait between two loops execution
* @param waitTime time in milliseconds to wait between two loops execution, will only be used if waitOnLoop is true
* @param startSuspended if true then the execution will be paused once the {@link #start()} method is called until a call to the {@link #resume()} method
*/
public ThreadTemplate(boolean waitOnLoop, int waitTime, boolean startSuspended) {
this(false, waitOnLoop, waitTime, startSuspended);
}
/**
* Constructor allowing to choose all the initialisation values.
* @param executeOnce true to execute the stuff to do only once, false will make it executed until a call to {@link #stop()} or until {@link #shouldEnd()} returns true.
* @param waitOnLoop boolean telling if a sleep must be done between each execution of the running loop
* @param waitTime time in milliseconds to sleep between each execution of the running loop, only used if waitOnLoop is true
* @param startSuspended
*/
public ThreadTemplate(boolean executeOnce, boolean waitOnLoop, int waitTime, boolean startSuspended) {
loopWaitTime = waitTime;
executeOnlyOnce = executeOnce;
threadSuspended = startSuspended;
mustWait = waitOnLoop;
}
/**
* Starts/restart the execution of the ThreadTemplate
*/
public void start() {
stop();
init();
t = new Thread(new ThreadTemplateRunnable());
t.start();
}
/**
* Stops definitely the execution of the ThreadTemplate
*/
public void stop() {
t = null;
synchronized (waiter) {
waiter.notify();
}
synchronized (this) {
notify();
}
}
/**
* Makes the ThreadTemplate to pause until the {@link #resume()} method is called
*/
public void pause() {
threadSuspended = true;
}
/**
* Makes the ThreadTemplate to continue its execution after a call to {@link #pause()}
*/
public void resume() {
threadSuspended = false;
synchronized (waiter) {
waiter.notify();
}
}
/**
* Tells if the inner thread of the ThreadTemplate is still alive or not.
* @return true if the inner thread is still alive.
*/
public boolean isAlive() {
return (t != null) && (t.isAlive());
}
/**
* Return the pause status of the ThreadTemplate
* @return a boolean value indicating the pause status of the ThreadTemplate
*/
public boolean isPaused() {
return threadSuspended;
}
/**
* Method used to stop the loop of execution if the ThreadTemplate is not declared as executing only once.
* By default it returns false to have an infinite loop.
* @return true to stop the loop
*/
protected boolean shouldEnd() {
return false;
}
/**
* Sample initialisation, override to do something more useful
*/
protected void init() {
}
/**
* Method that need to be overridden to do the required job.<br />
* This method is executed several times if the ThreadTemplate is not declared as executing only once.
*/
protected abstract void doIt();
/**
* @return the defined time in milliseconds to wait between two execution loops if {@link #mustWaitOnLoop()} is true.
*/
public int getLoopWaitTime() {
return loopWaitTime;
}
/**
* Defines the time to wait between two execution loops.
* @param loopWaitTime time in milliseconds to wait, if <= 0 then it is considered like a non waiting mode, i.e. calling {@link #setLoopWaitTime(int)} with a zero or negative value is the same as calling {@link #setMustWaitOnLoop(boolean)} with a false value
*/
public void setLoopWaitTime(int loopWaitTime) {
if (loopWaitTime <= 0) {
this.loopWaitTime = DEFAULT_LOOP_WAIT;
this.mustWait = false;
} else {
this.loopWaitTime = loopWaitTime;
}
}
/**
* @return true if the ThreadTemplate is configured to wait between two execution loops.
*/
public boolean mustWaitOnLoop() {
return mustWait;
}
/**
* Defines if the ThreadTemplate must wait between two execution loops.
* @param mustWait true to make the ThreadTemplate object waiting between two execution loops.
*/
public void setMustWaitOnLoop(boolean mustWait) {
this.mustWait = mustWait;
}
/**
* This the Runnable class used to do the stuff.
* It contains all the logic for pause/resume/stop the Thread execution.<br />
* Let's read @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html">Thread deprecation page</a>, for more explanation on the following code.
*/
private class ThreadTemplateRunnable implements Runnable {
public void run() {
if (executeOnlyOnce) {
doIt();
} else {
Thread thisThread = Thread.currentThread();
while (t == thisThread && !shouldEnd()) {
// First we check if we need to suspend execution
try {
synchronized(waiter) {
while (threadSuspended && t==thisThread) {
waiter.wait();
}
}
} catch (InterruptedException e){}
// We then do the stuff, the test is needed in order to not execute
// the stuff after a call to stop if the ThreadTemplate was paused
if (!threadSuspended && t==thisThread && !shouldEnd()) {
doIt();
}
// If we must wait for some time between two executions of the loop, we do it now
if (mustWait) {
try {
Thread.sleep(loopWaitTime);
} catch (InterruptedException ie) {}
}
}
}
// We enforce a good end to the ThreadTemplate, we will call a notify on waiters
stop();
}
}
} |
Partager