Lorsque vous souhaitez qu'une certaine tâche soit exécutée par un autre thread, vous pouvez étendre Thread ou implémenter Runnable.Java: classe entièrement exécutée dans le second thread/IllegalMonitorStateException
J'ai essayé de créer une classe qui exécute une classe entièrement dans le deuxième thread. Cela signifie que vous pouvez appeler anyMethod() qui retourne immédiatement et qui est exécuté par le second thread.
Voici ma tentative:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Extend this class to run method calls asynchronously in the second thread implemented by this class.
* Create method(type1 param1, type2 param2, ...) and let it call this.enqueueVoidCall("method", param1, param2, ...)
*
* The thread executing the run-method will automatically call methodAsync with the specified parameters.
* To obtain the return-value, pass an implementation of AsyncCallback to this.enqueueCall().
* AsyncCallback.returnValue() will automatically be called upon completion of the methodAsync.
*
*/
public class ThreadedClass extends Thread {
private static Object test;
private Queue<String> queue_methods = new ConcurrentLinkedQueue<String>();
private Queue<Object[]> queue_params = new ConcurrentLinkedQueue<Object[]>();
private Queue<AsyncCallback<? extends Object>> queue_callback = new ConcurrentLinkedQueue<AsyncCallback<? extends Object>>();
private volatile boolean shutdown = false;
/**
* The run method required by Runnable. It manages the asynchronous calls placed to this class.
*/
@Override
public final void run() {
test = new Object();
while (!shutdown) {
if (!this.queue_methods.isEmpty()) {
String crtMethod = queue_methods.poll();
Object[] crtParamArr = queue_params.poll();
String methodName = crtMethod + "Async";
Method method;
try {
method = this.getClass().getMethod(methodName);
try {
Object retVal = method.invoke(this, crtParamArr);
AsyncCallback<? extends Object> crtCallback = queue_callback.poll();
crtCallback.returnValue(retVal);
} catch (Exception ex) {}
} catch (SecurityException ex) {
} catch (NoSuchMethodException ex) {}
} else {
try {
synchronized(test) {
test.wait();
}
} catch (InterruptedException ex) {
System.out.println("READY");
} catch (Exception ex) {
System.out.println("READY, but " + ex.getMessage());
}
}
}
}
/**
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters
* @param methodName The name of the currently called method. methodName + "Async" is being called
* @param parameters Parameters you may want to pass to the method
*/
protected final void enqueueVoidCall(String methodName, Object... parameters) {
List<Object> tmpParam = new ArrayList<Object>();
for (Object crt : parameters) {
tmpParam.add(crt);
}
queue_methods.add(methodName);
queue_params.add(parameters);
queue_callback.add(null);
test.notifyAll();
}
/**
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters
* @param methodName The name of the currently called method. methodName + "Async" is being called
* @param callBack An instance of AsyncCallback whose returnValue-method is called upon completion of the task.
* @param parameters Parameters you may want to pass to the method
*/
protected final void enqueueCall(String methodName, AsyncCallback<? extends Object> callBack, Object... parameters) {
List<Object> tmpParam = new ArrayList<Object>();
for (Object crt : parameters) {
tmpParam.add(crt);
}
queue_methods.add(methodName);
queue_params.add(parameters);
queue_callback.add(callBack);
test.notifyAll();
}
/**
* Complete the currently running task, optionally return values and eventually shut down. The instance of this object becomes unusable after this call.
*/
public void shutdown() {
shutdown=true;
}
}
Maintenant, j'ai deux classes pour tester les choses:
public class MySecondTask extends ThreadedClass {
public void test1() {
this.enqueueVoidCall("test1", null);
}
public void test1Async() {
System.out.println("Start");
try {
// do big job here
} catch (Exception ex) { }
System.out.println("Done");
}
}
Et la principale méthode à partir de la substance:
public class TestingClass {
public static void main(String[] args) {
MySecondTask test = new MySecondTask();
test.start();
System.out.println("1. Thread [1]");
// CORRECTION, SHOULD BE:
test.test1();
// INSTEAD OF:
// test.test1Async();
for(int q=0; q<=100000; q++) {
System.out.println("1:"+ new Date().getTime()+":"+ q);
if ((q % 1000) == 0) {
System.out.flush();
}
}
System.err.println("1. Thread [2]");
}
}
D'une certaine façon, la sortie du deuxième thread apparaît toujours en premier (entièrement), puis le reste sort sur la console. Si les threads étaient en cours d'exécution (ce qui est le résultat escompté), la sortie console devrait être mélangée ?!
Toute idée est appréciée ainsi que des commentaires pour améliorer mon style de codage.
EDIT:
Le problème cité est tout à fait résolu.
Maintenant, je reçois une exception IllegalMonitorStateException sur la ligne où j'appelle: ThreadedClass.notifyAll().
Peut-être que j'ai eu celui avec le verrou faux. A) Pourquoi est-il nécessaire d'utiliser synchronized() autour de wait() et comment puis-je faire l'appel notifyAll() pour débloquer wait()?
grâce à l'avance et meilleures salutations
p.s .: vous tous faire un bon travail sur le débordement de la pile. tu m'as déjà aidé plusieurs fois sans le savoir, merci pour ça. continuez!
Y at-il une erreur dans TestingClass.main()? Je ne vois nulle part qu'il appelle test1(). –
Attendez une minute, où avez-vous obtenu ceci: objet AsyncCallback, il ne fait pas partie de Java – OscarRyz