I'm trying to figure out for which purposes using volatile is more convenient than locking?
It is a more convenient1 solution in simple cases where the volatile is the sole state that needs to be synchronized, and the operations are intrinsically atomic.
For example:
public class Processor extends Thread {
private volatile stopped;
public void run() {
while (!stopped) {
// do stuff
}
}
public void stop() {
stopped = true;
}
}
public class Test {
public static void main(String[] args) {
Processor p = new Processor();
p.start();
Thread.sleep(1000);
p.stop();
}
}
If you use a regular variable for stopped
then you need to use synchronized
(or some other form or locking) when reading and writing it ... which is more code and less convenient.
public class Processor extends Thread {
private stopped;
public void run() {
while (!isStopped()) {
// do stuff
}
}
public synchronized void stop() {
stopped = true;
}
private synchronized boolean isStopped() {
return stopped;
}
}
However, this only works as an alternative to locking in simple cases. If the protected state requires more than atomic reads and atomic writes then it will not work. For example:
public class Counter {
private volatile int counter;
public void increment() {
counter = counter + 1;
}
private int get() {
return counter;
}
}
The above is NOT thread safe because of the way increment()
is implemented. For correct (thread-safe) behavior, increment = increment + 1
needs to be done atomically. A volatile
does not provide that guarantee. The memory read
and write
operations are individually atomic, but they are not atomic as a sequence.
AFAIK, there is no thread-safe way to implement Counter
using just volatile
variables. You need either locks, or an AtomicInteger
(which typically relies on CAS hardware support ...)
1 - More convenient if you just count lines of code. If you also factor in the work that the code author and maintainers expends on reasoning that the solution is correct, I would argue that the "convenience" is largely an illusion.
Volatile is better than locking in which cases?
This is a more difficult question because "better" is a loaded term. But if you mean "more performant", then volatile
is generally better in situations where it works:
- A
volatile
read or write just results in a memory barrier.
- A locking operation is typically implemented using a CAS instruction and some code to deal with contention. The CAS introduces a memory barrier.
- An
Atomic
type is also typically implemented with a CAS.
This analysis is a bit crude, but the bottom like is that if volatile
is powerful to do the job, then it is likely to be more efficient than the alternatives. But there are two caveats:
- Most operations that involve inter-thread communication or synchronization are too complicated for
volatile
- Under normal circumstances, the performance advantage of
volatile
is too small to be significant. This changes if there is a contention or synchronization bottleneck.