Возвращаемое значение из Thread
У меня есть метод с 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();
}
Ответы
Ответ 1
Вы можете использовать локальный конечный переменный массив. Переменная должна быть не примитивного типа, поэтому вы можете использовать массив. Вам также необходимо синхронизировать два потока, например, используя 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();
}
Ответ 2
Обычно вы делаете это примерно так
public class Foo implements Runnable {
private volatile int value;
@Override
public void run() {
value = 2;
}
public int getValue() {
return value;
}
}
Затем вы можете создать поток и получить значение (при условии, что значение было установлено)
Foo foo = new Foo();
Thread thread = new Thread(foo);
thread.start();
thread.join();
int value = foo.getValue();
tl;dr
поток не может вернуть значение (по крайней мере, без механизма обратного вызова). Вы должны ссылаться на поток как обычный класс и запрашивать значение.
Ответ 3
То, что вы ищете, возможно, является интерфейсом 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();
}
Ответ 4
Как насчет этого решения?
Он не использует класс 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()
на нем до его завершения, поток, вызывающий его, просто будет ждать, пока он не будет выполнен
Ответ 5
Если вы хотите получить значение из вызывающего метода, тогда он должен дождаться окончания потока, что делает использование потоков немного бессмысленным.
Чтобы напрямую ответить на вопрос, значение может быть сохранено в любом изменяемом объекте, как вызывающий метод, так и поток, на который есть ссылка. Вы можете использовать внешний this
, но это не будет особенно полезно, кроме как для тривиальных примеров.
Небольшое примечание к коду в вопросе: Расширение Thread
обычно плохое. Действительно, расширение классов без необходимости - плохая идея. Я заметил, что метод run
по какой-то причине синхронизирован. Теперь, поскольку объектом в этом случае является Thread
, вы можете вмешиваться в то, что Thread
использует свою блокировку для (в ссылочной реализации, что-то делать с join
, IIRC).
Ответ 6
Использование Future, описанное в приведенных выше ответах, выполняет задание, но немного менее важно, поскольку f.get() блокирует поток до тех пор, пока не получит результат, который нарушает concurrency.
Лучшим решением является использование Guava 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)
{
}
};
Ответ 7
Java 8 предоставляет CompletableFuture. это будет единственное решение для этого. http://www.baeldung.com/java-completablefuture
Ответ 8
С небольшими изменениями в вашем коде вы можете достичь этого более общим образом.
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
, разместите сообщение на этом responseHandler
-
handleMessgae
показывает a Toast
со значением, полученным от сообщения. Этот объект Message является общим и вы можете отправлять атрибуты разных типов.
При таком подходе вы можете отправить несколько значений в поток пользовательского интерфейса в разные моменты времени. Вы можете запускать (публиковать) много объектов Runnable
на этом HandlerThread
, и каждый Runnable
может установить значение в объекте Message
, который может быть получен в потоке пользовательского интерфейса.