The use of a volatile
flag, as presented in the question, brings no benefit other than a sensation on the part of the developer to have in their hands the "control" of execution. :)
The Java Threads API has implemented a generic usage flag to manage "paused" thread breaks.
If a thread is paused in a call to Object#wait()
, Thread#join
or Thread#sleep
, the interrupt()
is able to" wake "it. In practice, it is as if an error occurred in calling one of these methods and it throws the InterruptedException
exception.
Certain input and output (I / O) operations may also be affected, for example those using InterruptibleChannel
.
The implementation of the interrupt flag is native, so the JVM can use more optimized mechanisms for communication between threads.
Therefore, if used correctly, the API provides advanced mechanisms for stopping the execution of a thread even when there are time-consuming operations being performed, such as file and network access.
Interruptions and loops
Such a flag is also commonly used to break links , as in the example question.
The only danger in doing this is not to control the state of the interrupt flag properly.
For example, if you handle the interrupt exception:
public void run() {
while(!Thread.interrupted()) {
// Começa algo
try {
// Faz algo que pode lançar InterruptedException
} catch (InterruptedException e) {
log.error(e);
}
// Finaliza algo
}
}
The code above will result in an infinite loop because catching the exception clears the flag and the loop exit condition will always be false!
Because of this, if you need to catch such an exception, in general it is recommended that if you restore the interrupt in catch
, like this:
public void run() {
while(!Thread.interrupted()) {
// Começa algo
try {
// Faz algo que pode lançar InterruptedException
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error(e);
}
// Finaliza algo
}
}
As the exception handling may be "hidden" within another method, some cautiously recommend that you always call interrupt
within a catch
that captures such an exception.
Flag volatile
Manually controlling a flag has some disadvantages, among which I consider the worst:
- Variable sharing is required. Threads need to communicate directly, increasing the coupling.
- Inability to interrupt waiting threads.
- Developers who do not understand
volatile
. Some try to imitate the use and forget the modifier, which can lead to unexpected race conditions and behavior.
On the other hand, there are advantages to this approach:
- If the flag is not simply a
boolean
, that is, if there is any more complex object that is shared. For example, in the case of threads consuming a queue, the "flag" may be the queue being empty.
- If an arbitrary number of threads depends on the same flag.
- If it is not desired that immediate interruption of the current iteration of the thread does not occur. It may be, for example, that the goal is just to end the loop, but any operation waiting for
wait
or join
should continue until the end.
- The use of a flag in a trivial loop as the issue is simpler to understand compared to the API.
Confused API
Unfortunately, the Java interrupt control API is somewhat confusing. Compare available methods:
-
Thread.interrupt()
: Stops a thread. It should be called directly on an object, which can be the current thread (as in the example above) or some other thread in case you want to interrupt it. This method activates the interrupt flag.
-
Thread.isInterrupted
(): checks whether the interrupt flag is active for a given thread. It should also be called on an object, which may be the current thread or another thread.
-
Thread.interrupted()
: returns the state of the interrupt flag and clears the flag . This is a static method of the class, so it is called without an object and always references the current thread.
The first two methods make a lot of sense, but the last one is a bit strange, because once it is invoked it effectively clears the flag. this leads to unusual situations such as when a Boolean expression used in a if
modifies the state and the following code "sees" other values.
Considerations
My suggestion is to use the API by default when this applies to the problem. If there is a more specific need, a manual solution is needed and there is no problem with this.
It is not wrong to use a flag, but for a mature developer it is important to learn how to use the Java API - or whatever language it uses. This is part of avoiding ad hoc solutions when possible.