I have a java client which is managing its server calls in new threads to prevent the GUI from freezing.
Even if this is prevented in many places, it is possible that the method will be called again on the same model, for example with different parameters. In such case, I obviously want the newest call, with the most up to da开发者_高级运维te parameters, to be the one "succeeding" and displaying its results.
I have a system which is keeping track of the previously launched Thread, and interrupts it before launching the new one (Thread.interrupt()
). The other methods are then checking if they are running in a non-interrupted thread (using if (Thread.currentThread().isInterrupted()
) before firing the new results to the GUI elements.
This structure was working with a previous connector to the server, as I was the only one checking the interrupt flag on the process. My problem is that I'm now using EJB method calls in the client, and they react badly to the interrupted thread. Interrupting the Thread during an EJB call will trigger a RuntimeException
which is including an InterruptedException
. And it doesn't seem like a normal thing to happen.
Obviously I could catch RuntimeExceptions in every server call and check their cause for the interrupt, but it doesn't seem very "clean".
My question is: what can I do in such context? What is the proper way to interrupt a Thread running an EJB method call?
Seeing as you don't mind the obsolete EJB call continuing on the server, why not allow the calling thread to terminate 'naturally' but to discard the result because its call has been superceded by another thread? I don't have time to provide a sample implementation, but you may find that you get some mileage with Future
s and the related Java concurrency classes.
Edit
Further to this, you may find something like this would do the trick, but it feels hacky to me and I'm sure there are more elegant solutions.
On the calling thread (perhaps the onclick method of a button):
AsynchronousResultManager.registerRequest("UNIQUE_IDENTIFIER", runnableExecuteRequest);
The registerRequest
would do something like:
registerClick(String id, Runnable execution) {
AtomicReference ref = executions.get(id); //executions is a Map<String, AtomicReference> created as a a computing map by Guava MapMaker
execution.setReference(ref); //so that the Runnable has a reference to it later
ref.set(execution); //this will overwrite an existing reference to a previous invocation.
//here you need to actually kick off your thread in whatever way works best for you
}
The runnable
that executes the request would be a subclass of:
public abstract class RequestRunnable implements Runnable {
private AtomicReference ref;
public void run() {
doRunInternal(); //actually go off and do the request to the J2EE server
if (this == ref.get()) { //ie if the current runnable is the same as in the reference, we can proceed to actually dispatch the result
dispatchResult(); //this method would do something like add a runnable to the SwingWorkerThread
}
}
protected abstract void doRunInternal();
protected abstract void dispatchResult();
public void setReference(AtomicReference ref) {
this.ref = ref;
}
}
This will probably crash and burn, but hopefully it points you down a line of enquiry...
To stop a thread you need to perform 2 sort of actions :
- if the thread is waiting for a blocking operation (IO, network, lock...) you need to interupt it. An InterruptedException will be thrown giving the running code the opportunity to catch the exception and to halt in a proper way.
- If the thread is just doing some processing, Thread.interrupt() will not help. No exception will be thrown and the thread will simply continue it's processing. The processing code will need to check on a regular basis that you still want the process to continue.
In any case, to do it properly, you need that the code that is run by your thread that you want to stop deal with the both cases. No silver bullet here.
Given the existing architecture, I finally went with catching the RuntimeException from each server call, in such form:
try {
return getEJBService().getServiceResult(param, param, param);
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof InterruptedException)
throw (InterruptedException)cause;
else
throw e;
}
It's not really pretty, but at least it allows me to act according to the interruption with my current model.
In the ideal world, one should rather go with one of the solutions given by Rich.
精彩评论