J'ai une méthode avec un HandlerThread
. Une valeur est modifiée dans la Thread
et j'aimerais la renvoyer à la méthode test()
. Y a-t-il un moyen de faire cela?
public void test()
{
Thread uiThread = new HandlerThread("UIHandler"){
public synchronized void run(){
int value;
value = 2; //To be returned to test()
}
};
uiThread.start();
}
Vous pouvez utiliser un tableau de variables final local. La variable doit être de type non primitif pour que vous puissiez utiliser un tableau. Vous devez également synchroniser les deux threads, par exemple en utilisant un CountDownLatch :
public void test()
{
final CountDownLatch latch = new CountDownLatch(1);
final int[] value = new int[1];
Thread uiThread = new HandlerThread("UIHandler"){
@Override
public void run(){
value[0] = 2;
latch.countDown(); // Release await() in the test thread.
}
};
uiThread.start();
latch.await(); // Wait for countDown() in the UI thread. Or could uiThread.join();
// value[0] holds 2 at this point.
}
Vous pouvez également utiliser un Executor
et un Callable
comme ceci:
public void test() throws InterruptedException, ExecutionException
{
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() {
return 2;
}
};
Future<Integer> future = executor.submit(callable);
// future.get() returns 2 or raises an exception if the thread dies, so safer
executor.shutdown();
}
Habituellement, vous feriez quelque chose comme ça
public class Foo implements Runnable {
private volatile int value;
@Override
public void run() {
value = 2;
}
public int getValue() {
return value;
}
}
Ensuite, vous pouvez créer le thread et récupérer la valeur (étant donné que la valeur a été définie)
Foo foo = new Foo();
Thread thread = new Thread(foo);
thread.start();
thread.join();
int value = foo.getValue();
tl;dr
un thread ne peut pas renvoyer de valeur (du moins sans mécanisme de rappel). Vous devriez référencer un thread comme une classe ordinaire et demander la valeur.
Ce que vous recherchez, c'est probablement l'interface Callable<V>
à la place de Runnable
et la récupération de la valeur avec un objet Future<V>
, ce qui vous permet également d'attendre que la valeur ait été calculée. Vous pouvez y parvenir avec un ExecutorService
que vous pouvez obtenir à partir de Executors.newSingleThreadExecutor()
.
public void test() {
int x;
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Integer> result = es.submit(new Callable<Integer>() {
public Integer call() throws Exception {
// the other thread
return 2;
}
});
try {
x = result.get();
} catch (Exception e) {
// failed
}
es.shutdown();
}
Que diriez-vous de cette solution?
Il n'utilise pas la classe Thread, mais il IS simultané et fait en quelque sorte ce que vous demandez.
ExecutorService pool = Executors.newFixedThreadPool(2); // creates a pool of threads for the Future to draw from
Future<Integer> value = pool.submit(new Callable<Integer>() {
@Override
public Integer call() {return 2;}
});
Maintenant, tout ce que vous faites est de dire value.get()
chaque fois que vous devez saisir votre valeur renvoyée, le fil est démarré à la seconde où vous donnez à value
une valeur afin que vous n'ayez jamais à dire threadName.start()
. dessus.
Qu'est-ce qu'un Future
est, est un promesse au programme, vous promettez au programme que vous obtiendrez la valeur dont il a besoin dans un avenir proche
Si vous appelez .get()
avant la fin de l'opération, le fil qui l'appelle attendra simplement jusqu'à ce que l'opération soit terminée.
Si vous voulez la valeur de la méthode d'appel, attendez que le thread se termine, ce qui rend l'utilisation des threads un peu inutile.
Pour répondre directement à votre question, la valeur peut être stockée dans n’importe quel objet mutable, la méthode d’appel et le thread ayant tous deux une référence. Vous pouvez utiliser la variable this
extérieure, mais cela ne sera pas particulièrement utile, sauf pour des exemples triviaux.
Une petite note sur le code dans la question: Étendre Thread
est généralement un style médiocre. En effet, prolonger les cours inutilement est une mauvaise idée. Je remarque que votre méthode run
est synchronisée pour une raison quelconque. Maintenant, comme l'objet dans ce cas est le Thread
vous pouvez interférer avec tout ce que Thread
utilise son verrou pour (dans l'implémentation de référence, quelque chose à voir avec join
, IIRC).
Java 8 fournit CompletableFuture. ce sera la solution unique pour cela. http://www.baeldung.com/Java-completablefuture
Utiliser Future décrit dans les réponses ci-dessus fait le travail, mais un peu moins significatif que f.get (), bloque le thread jusqu'à ce qu'il obtienne le résultat, ce qui viole l'accès simultané.
La meilleure solution consiste à utiliser ListenableFuture de Guava. Un exemple :
ListenableFuture<Void> future = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1, new NamedThreadFactory).submit(new Callable<Void>()
{
@Override
public Void call() throws Exception
{
someBackgroundTask();
}
});
Futures.addCallback(future, new FutureCallback<Long>()
{
@Override
public void onSuccess(Long result)
{
doSomething();
}
@Override
public void onFailure(Throwable t)
{
}
};
Avec de petites modifications à votre code, vous pouvez y parvenir de manière plus générique.
final Handler responseHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
//txtView.setText((String) msg.obj);
Toast.makeText(MainActivity.this,
"Result from UIHandlerThread:"+(int)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){
public void run(){
Integer a = 2;
Message msg = new Message();
msg.obj = a;
responseHandler.sendMessage(msg);
System.out.println(a);
}
};
handlerThread.start();
Solution :
Handler
dans le thread de l'interface utilisateur, appelé responseHandler
.Handler
à partir de Looper
du thread UI.HandlerThread
, postez un message sur cette responseHandler
handleMessgae
affiche un Toast
avec la valeur reçue du message. Cet objet Message est générique et vous pouvez envoyer différents types d'attributs.Avec cette approche, vous pouvez envoyer plusieurs valeurs à un thread d'interface utilisateur à des moments différents. Vous pouvez exécuter (publier) de nombreux objets Runnable
sur ce HandlerThread
et chaque Runnable
peut définir une valeur dans un objet Message
pouvant être reçu par UI Thread.