从Thread返回值

时间:2012-02-05 11:36:08

标签: java android multithreading

我有一个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();
}

10 个答案:

答案 0 :(得分:65)

通常你会这样做

 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线程无法返回值(至少没有回调机制)。您应该像普通类一样引用一个线程并询问该值。

答案 1 :(得分:65)

您可以使用本地最终变量数组。变量必须是非基本类型,因此您可以使用数组。您还需要同步两个线程,例如使用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.
}

你也可以使用这样的ExecutorCallable

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 :(得分:26)

您正在寻找的可能是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();
}

答案 3 :(得分:6)

这个解决方案怎么样?

它不使用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() 1}}就可以了。

该程序的Future承诺,您承诺程序在不久的将来有时会获得它所需的价值

如果你在它完成之前打电话给.get(),那么正在调用它的线程就会等到它完成之后

答案 4 :(得分:4)

如果你想要调用方法的值,那么它应该等待线程完成,这使得使用线程有点无意义。

要直接回答你的问题,可以将值存储在任何可变对象中,调用方法和线程都有引用。你可以使用外部this,但除了琐碎的例子之外,这不会特别有用。

关于问题代码的一点注意事项:扩展Thread通常是糟糕的风格。实际上,不必要地扩展类是一个坏主意。我注意到你run方法由于某种原因被同步。现在,作为此案例中的对象是Thread,您可能会干扰Thread使用其锁定的任何内容(在参考实现中,与join,IIRC有关。)

答案 5 :(得分:1)

使用上述答案中描述的Future完成了这项工作,但是f.get()稍微不那么明显,阻塞了线程,直到它获得结果,这违反了并发性。

最佳解决方案是使用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)
        {

        }
    };

答案 6 :(得分:1)

Java 8提供CompletableFuture。这将是一站式解决方案。 http://www.baeldung.com/java-completablefuture

答案 7 :(得分:0)

通过对代码的少量修改,您可以以更通用的方式实现它。

 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. 在UI线程中创建Handler,称为responseHandler
  2. 从UI线程的Handler初始化此Looper
  3. HandlerThread中,在此responseHandler
  4. 上发布消息
  5. handleMessgae显示Toast,其中包含从邮件中收到的值。此Message对象是通用的,您可以发送不同类型的属性。
  6. 使用此方法,您可以在不同的时间点向UI线程发送多个值。您可以在此Runnable上运行(发布)许多HandlerThread个对象,并且每个Runnable都可以在Message对象中设置值,这可以由UI线程接收。

答案 8 :(得分:0)

这是一种更简洁的方法,您只需要对现有代码进行一些更改。目标是从线程中获取结果。它不一定是 return 结果。相反,使用回调样式来获取该结果并进行进一步处理。

public class Test {

  public static void main(String[] args) {
    String str = args[0];
    int count = 0;

    Thread t = new Thread(() ->
      someFuncToRun(str, count, (value) -> {
        System.out.println(value);
        return value;
      }));

    t.start();
  }
  // Here I even run a recursive method because run things in the 
  // a thread sometime is to delegate those heavy lifting elsewhere
  public static String someFuncToRun(String str, int ctn, Callback<String> p) {
    ++ctn;
    if (ctn == 10) {
      System.out.println("End here");
      return p.cb(str);
    }
    System.out.println(ctn + " times");
    return someFuncToRun(str + " +1", ctn, p);
  }
}

// The key is here, this allow you to pass a lambda callback to your method 
// update: use generic to allow passing different type of data
// you could event make it <T,S> so input one type return another type  
interface Callback<T> {
    public T cb(T a);
}

答案 9 :(得分:0)

您可以使用 java.util.concurrent.atomic 中的类,例如 AtomicInteger。

public static void main(String[] args) throws InterruptedException {
    AtomicInteger value = new AtomicInteger(0);
    Thread thread = new Thread(() -> value.set(2));
    thread.start();
    thread.join();
    System.out.println(value.get());
}