Возвращаемое значение из потока



у меня есть метод с 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();
}
543   8  

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();

устранение :

  1. создать Handler в потоке пользовательского интерфейса,который называется responseHandler
  2. инициализировать этот Handler С Looper из потока пользовательского интерфейса.
  3. на HandlerThread, сообщением об этом responseHandler
  4. handleMessgae показывает a Toast С значением, полученным из сообщения. Этот объект сообщения является универсальным, и вы можете отправить другой тип из атрибутов.

при таком подходе вы можете отправлять несколько значений в поток пользовательского интерфейса в разные моменты времени. Вы можете запустить (пост) много Runnable объекты на этой HandlerThread и друг Runnable можно установить значение в Message объект, который может быть получен потоком пользовательского интерфейса.

Java 8 обеспечивает CompletableFuture . это будет одно решение остановки для этого. http://www.baeldung.com/java-completablefuture

Comments

    Ничего не найдено.