Mam metodę z rozszerzeniem HandlerThread
. Wartość zostaje zmieniona wewnątrz Thread
i chciałbym zwrócić ją do test()
metody. Czy jest na to sposób?
public void test()
{
Thread uiThread = new HandlerThread("UIHandler"){
public synchronized void run(){
int value;
value = 2; //To be returned to test()
}
};
uiThread.start();
}
java
android
multithreading
Neeta
źródło
źródło
Odpowiedzi:
Możesz użyć lokalnej końcowej tablicy zmiennych. Zmienna musi być typu innego niż pierwotny, aby można było użyć tablicy. Musisz także zsynchronizować dwa wątki, na przykład za pomocą 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. }
Możesz również użyć znaków
Executor
i wCallable
ten sposób: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(); }
źródło
Zwykle zrobiłbyś to coś takiego
public class Foo implements Runnable { private volatile int value; @Override public void run() { value = 2; } public int getValue() { return value; } }
Następnie możesz utworzyć wątek i pobrać wartość (zakładając, że wartość została ustawiona)
Foo foo = new Foo(); Thread thread = new Thread(foo); thread.start(); thread.join(); int value = foo.getValue();
tl;dr
wątek nie może zwrócić wartości (przynajmniej nie bez mechanizmu wywołania zwrotnego). Powinieneś odwołać się do wątku jak zwykłej klasy i zapytać o wartość.źródło
The method getValue() is undefined for the type Thread
.t.getValue()
nafoo.getValue()
.Thread t = new Thread(foo); t.start(); t.join(); foo.getValue();
. Tet.join()
bloki, aż gwint jest zakończona.To, czego szukasz, to prawdopodobnie
Callable<V>
interfejs zamiastRunnable
i pobieranie wartości za pomocąFuture<V>
obiektu, co pozwala również czekać, aż wartość zostanie obliczona. Możesz to osiągnąć za pomocąExecutorService
, z którego możesz uzyskać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(); }
źródło
A co z tym rozwiązaniem?
Nie używa klasy Thread, ale JEST współbieżna iw pewien sposób robi dokładnie to, o co prosisz
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;} });
Teraz wszystko, co musisz zrobić, to powiedzieć, że
value.get()
za każdym razem, gdy chcesz pobrać zwróconą wartość, wątek jest uruchamiany w tej samej sekundzie, w której podajeszvalue
wartość, więc nigdy nie musisz o tym mówićthreadName.start()
.Co to
Future
jest, to obietnica dla programu, obiecujesz programowi, że dostaniesz mu potrzebną wartość w najbliższej przyszłościJeśli wywołasz
.get()
go przed zakończeniem, wątek, który go wywołuje, po prostu zaczeka, aż się skończyźródło
Jeśli chcesz uzyskać wartość z metody wywołującej, powinieneś poczekać na zakończenie wątku, co sprawia, że używanie wątków jest trochę bezcelowe.
Aby bezpośrednio odpowiedzieć na twoje pytanie, wartość może być przechowywana w dowolnym zmiennym obiekcie, zarówno metoda wywołująca, jak i wątek mają odniesienie do. Możesz użyć zewnętrznego
this
, ale nie będzie to szczególnie przydatne poza trywialnymi przykładami.Mała uwaga na temat kodu w pytaniu: Rozszerzanie
Thread
jest zwykle kiepskim stylem. Rzeczywiście, niepotrzebne rozszerzanie klas to zły pomysł. Zauważyłem, żerun
z jakiegoś powodu metoda jest zsynchronizowana. Ponieważ obiekt w tym przypadku jest tymThread
, możesz ingerować we wszystko, do czegoThread
używa jego blokady (w implementacji referencyjnej ma to coś wspólnego zjoin
IIRC).źródło
Od wersji Java 8 mamy
CompletableFuture
. W twoim przypadku możesz użyć metody,supplyAsync
aby uzyskać wynik po wykonaniu.Proszę znaleźć jakieś odniesienie tutaj .
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> yourMethod()); completableFuture.get() //gives you the value
źródło
Korzystanie z Future opisanego w powyższych odpowiedziach spełnia swoje zadanie, ale nieco mniej istotnie, ponieważ f.get () blokuje wątek do momentu uzyskania wyniku, co narusza współbieżność.
Najlepszym rozwiązaniem jest skorzystanie z ListenableFuture firmy Guava. Przykład :
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) { } };
źródło
Dzięki niewielkim modyfikacjom kodu możesz to osiągnąć w bardziej ogólny sposób.
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();
Rozwiązanie :
Handler
wątek w interfejsie użytkownika, który nosi nazwęresponseHandler
Handler
zLooper
wątku interfejsu użytkownika.HandlerThread
, napisz wiadomość na ten tematresponseHandler
handleMessgae
pokazujeToast
wartość otrzymaną z wiadomości. Ten obiekt Message jest ogólny i można wysyłać różne typy atrybutów.Dzięki takiemu podejściu możesz wysyłać wiele wartości do wątku interfejsu użytkownika w różnych momentach. Możesz uruchomić (opublikować) wiele
Runnable
obiektów na tymHandlerThread
i każdyRunnable
może ustawić wartość wMessage
obiekcie, którą może odebrać UI Thread.źródło