2012-02-05 62 views
60

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

这是什么语言吗? – 2012-02-05 11:38:01

+0

如果主线程必须等待处理线程在从方法返回之前完成,为什么首先使用处理线程呢? – 2012-02-05 11:43:14

+0

@JBNizet我没有包含Thread实际所做的复杂性。它获得gps坐标,所以是的,我确实需要一个线程。 – Neeta 2012-02-05 11:59:26

回答

47

您可以使用本地最终变量数组。该变量需要是非原始类型的,所以你可以使用一个数组。您还需要使用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(); 
} 
+1

嗯......不。此代码不正确。访问值不正确同步。 – 2014-06-14 14:39:04

+3

由于CountDownLatch的内存一致性保证,我们实际上并不需要显式同步访问值。值数组的创建发生在uiThread启动(程序顺序规则)之前 - 与赋值2到值[0]([thread start](http://docs.oracle.com/javase/specs/jls/se7) /html/jls-17.html#jls-17.4.4))在latch.await()(发生在CountDownLatch的保证)之前发生的latch.countDown()(程序顺序规则)从值[0](程序顺序规则)。 – 2014-08-22 18:09:31

+0

看起来你是对的关于闩锁! ...在这种情况下,运行方法的同步是无用的。 – 2014-08-22 18:39:00

48

通常你会做这样的事情

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

+2

这真的有用吗?我得到'方法getValue()对于Thread类型是未定义的。 – pmichna 2013-11-26 15:07:08

+1

@pmichna,很好的发现。从't.getValue()'改为'foo.getValue()'。 – 2013-11-26 15:46:26

+0

@JohanSjöberg:Tnx为你的答案。但它有一个小错误。你应该叫“新线程(foo).start()”; – mostafa88 2014-05-29 08:37:41

21

您正在寻找的可能是代替RunnableCallable<V>接口,并通过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

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

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

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

+0

“那么它应该等待线程完成,这使得使用线程有点没有意义”伟大的一点! – likejiujitsu 2013-03-04 19:46:00

+3

它通常毫无意义,但在Android中,您无法向主线程上的服务器发出网络请求(以保持应用程序响应),因此您必须使用网络线程。 在应用程序可以恢复之前,有些情景需要结果。 – 2014-10-23 01:34:51

5

这个解决方案如何?

它不使用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()它完成之前,这是调用它只是简单地等待,直到它完成

+0

我将提供的代码分成两部分,一是我在Application类(我正在谈论android)中做的池初始化,其次我在需要它的地方使用池...另外关于使用Executors.newFixedThreadPool (2)我使用了Executors.newSingleThreadExecutor()..因为我只需要一次运行服务器调用的任务...你的答案是完美的@electirc咖啡谢谢 – 2016-12-10 10:07:58

1

使用未来在上述答案中描述的线程做的工作,但少了几分显著为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) 
     { 

     } 
    }; 
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线程,这被称为responseHandler
  2. 从UI线程的Looper初始化此Handler一个Handler
  3. HandlerThread,在此responseHandler
  4. handleMessgae交消息显示一个Toast从消息中接收的值。此Message对象是通用的,您可以发送不同类型的属性。

使用这种方法,您可以在不同的时间点向UI线程发送多个值。您可以运行该HandlerThread(POST)许多Runnable对象和每个Runnable可以Message的对象,可以通过UI线程接收设定值。

0

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

+1

这更像是一个反对评论的答案。您是否希望通过示例来加强它(甚至是从链接中复制并粘贴),以便一旦链接无效,人们仍然可以找到有用的答案。 – 2018-02-23 10:23:37