Возвращаемое значение из потока
у меня есть метод с HandlerThread. Значение изменяется внутри Thread и я хотел бы вернуть его в test() метод. Есть ли способ сделать это?
public void test()
{
Thread uiThread = new HandlerThread("UIHandler"){
public synchronized void run(){
int value;
value = 2; //To be returned to test()
}
};
uiThread.start();
}
8 ответов:
вы можете использовать локальный конечный массив переменных. Переменная должна быть непримитивного типа, так что вы можете использовать массив. Вам также необходимо синхронизировать два потока, например, используя 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. }вы также можете использовать
ExecutorиCallableтакой: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(); }
обычно вы бы сделали это что-то вроде этого
public class Foo implements Runnable { private volatile int value; @Override public void run() { value = 2; } public int getValue() { return value; } }затем вы можете создать поток и получить значение (учитывая, что значение было установлено)
Foo foo = new Foo(); new Thread(foo).start(); // ... join through some method int value = foo.getValue();
tl;drпоток не может вернуть значение (по крайней мере, не без механизма обратного вызова). Вы должны ссылаться на поток как обычный класс и запрашивать значение.
то, что вы ищете, вероятно,
Callable<V>интерфейс вместоRunnable, и получение значения с помощьюFuture<V>объект, который также позволяет вам ждать, пока значение не будет вычислено. Вы можете достичь этого с помощьюExecutorService, который вы можете получить от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(); }
как насчет такого решения?
он не использует класс Thread, но он параллелен, и в некотором смысле он делает именно то, что вы запрашиваете
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;} });теперь все, что вы делаете, это говорите
value.get()всякий раз, когда вам нужно захватить возвращенное значение, поток запускается в ту же секунду, когда вы даетеvalueзначение, так что вам никогда не придется говоритьthreadName.start()на нем.какой
Future- это обещание к программе, вы обещаете программе, что вы получите его значение это нужно когда-нибудь в ближайшем будущемесли вы называете
.get()на нем, прежде чем это будет сделано, поток, который его вызывает, просто подождет, пока это не будет сделано
если вы хотите получить значение из вызывающего метода, то он должен ждать завершения потока, что делает использование потоков немного бессмысленным.
чтобы напрямую ответить на ваш вопрос, значение может быть сохранено в любом изменяемом объекте, на который ссылаются как вызывающий метод, так и поток. Вы могли бы использовать внешний
this, но это не будет особенно полезен, кроме как для тривиальных примеров.небольшая заметка о коде в вопросе: расширение
Threadобычно плохой стиль. Действительно, расширение классов без необходимости-это плохая идея. Я замечаю тебяrunметод синхронизации по какой-то причине. Теперь в качестве объекта в этом случае являетсяThreadвы можете вмешиваться во что угодноThreadиспользует свою блокировку Для (в ссылочной реализации, что-то делать сjoin, IIRC).
использование будущего, описанного в приведенных выше ответах, выполняет эту работу, но немного менее значительно, как f.get (), блокирует поток, пока он не получит результат, что нарушает параллелизм.
лучшее решение-использовать ListenableFuture гуавы. Пример :
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) { } };
С небольшими изменениями в коде, вы можете достичь его в более общем виде.
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();устранение :
- создать
Handlerв потоке пользовательского интерфейса,который называетсяresponseHandler- инициализировать этот
HandlerСLooperиз потока пользовательского интерфейса.- на
HandlerThread, сообщением об этомresponseHandlerhandleMessgaeпоказывает aToastС значением, полученным из сообщения. Этот объект сообщения является универсальным, и вы можете отправить другой тип из атрибутов.при таком подходе вы можете отправлять несколько значений в поток пользовательского интерфейса в разные моменты времени. Вы можете запустить (пост) много
Runnableобъекты на этойHandlerThreadи другRunnableможно установить значение вMessageобъект, который может быть получен потоком пользовательского интерфейса.
Java 8 обеспечивает CompletableFuture . это будет одно решение остановки для этого. http://www.baeldung.com/java-completablefuture
Comments