Say, you want to allow a Java method to work for a maximum of five seconds and want an exception to be thrown if the timeframe is exceeded. Here is how you can do it with jcabi-aspects and AspectJ:
public class Resource {
@Timeable(limit = 5, unit = TimeUnit.SECONDS)
public String load(URL url) {
return url.openConnection().getContent();
}
}
Keep in mind that you should weave your classes after compilation, as explained here.
Let’s discuss how this actually works, but first, I recommend you read this post, which explains how AOP aspects work together with Java annotations.
Due to @Timeable
annotation and class weaving, every call to a method load()
is intercepted by an aspect from jcabi-aspects. That aspect starts a new thread that monitors the execution of a method every second, checking whether it is still running.
If the method runs for over five seconds, the thread calls interrupt()
on the method’s thread.
Despite a very common expectation that a thread should be terminated immediately on that call, it is not happening at all. This article explains the mechanism in more detail. Let’s discuss it briefly:
interrupt()
sets a marker in a thread;The thread checks
interrupted()
as often as it can;If the marker is set, the thread stops and throws
InterruptedException
This method will not react to interrupt()
call and will work until JVM is killed (very bad design):
public void work() {
while (true) {
// do something
}
}
This is how we should refactor it in order to make sensitive to interruption requests:
public void work() {
while (true) {
if (Thread.interruped()) {
throw new InterruptedException();
}
// do something
}
}
In other words, your method can only stop itself. Nothing else can do it. The thread it is running in can’t be terminated by another thread. The best thing that the other thread can do is to send your thread a “message” (through interrupt()
method) that it’s time to stop. If your thread ignores the message, nobody can do anything.
Most I/O operations in JDK are designed this way. They check the interruption status of their threads while waiting for I/O resources.
Thus, use @Timeable
annotation, but keep in mind that there could be situations when a thread can’t be interrupted.