I often use the following pattern to create a cancellable thread:
public class CounterLoop implements Runnable {
private volatile AtomicBoolean cancelPending = new AtomicBoolean(false);
@Override
public void run() {
while (!cancelPending.get()) {
//count
}
}
public void cancel() {
cancelPending.set(true);
}
}
But I'm not sure that cancelPending MUST be a AtomicBoolean. Can we just use a normal boolean in this case?
No, you can not. Because if you will change the boolean value from another thread without proper synchronization then this change can be invisible to another threads. You can use
valotile boolean
in your case to make any modification visible to all threads.Using a volatile boolean variable in this context is safe, though some may consider it bad practice. Consult this thread to see why.
Your solution of using an Atomic* variable seems the best option, even though the synchronization may introduce unnecessary overhead in comparison to a volatile variable.
You can also use a critical section
or a synchronized method.
Yes you can. You can either use a non volatile AtomicBoolean (relying on its built in thread safety), or use any other volatile variable.
According to the Java Memory Model (JMM), both options result in a properly synchronized program, where the read and write of the cancelPending variable can't produce a data race.
Using both
volatile
andAtomicBoolean
is unnecessary. If you declare thecancelPending
variable asfinal
as follows:the JLS semantics for
final
fields mean that synchronization (orvolatile
) will not be needed. All threads will see the correct value for thecancelPending
reference. JLS 17.5 states:... but there are no such guarantees for normal fields; i.e. not
final
and notvolatile
.You could also just declare
cancelPending
as avolatile boolean
... since you don't appear to be using the test-and-set capability ofAtomicBoolean
.However, if you used a non-volatile
boolean
you would need to usesynchronized
to ensure that all threads see an up-to-date copy of thecancelPending
flag.You can use a
volatile boolean
instead with no issues.Note that this only applies in cases much like this where the boolean is only being changed to a specific value (
true
in this case). If the boolean might be changed to eithertrue
orfalse
at any time then you may need anAtomicBoolean
to detect and act on race conditions.However - the pattern you describe has an innate smell. By looping on a boolean (
volatile
or not) you are likely to find yourself trying to insert some sort ofsleep
mechanism or having to interrupt your thread.A much cleaner route is to split up the process into finer steps. I recently posted an answer here covering the options of pausing threads that may be of interest.