从 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();
}
270337 次浏览

通常你会这样做

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

线程不能返回值(至少在没有回调机制的情况下不能)。您应该像引用普通类一样引用一个线程并请求该值。

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

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

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

为了直接回答您的问题,该值可以存储在调用方法和线程都引用的任何可变对象中。您可以使用外部的 this,但是除了对于琐碎的示例之外,它不会特别有用。

A little note on the code in the question: Extending Thread is usually poor style. Indeed extending classes unnecessarily is a bad idea. I notice you run method is synchronised for some reason. Now as the object in this case is the Thread you may interfere with whatever Thread uses its lock for (in the reference implementation, something to do with join, IIRC).

这个办法怎么样?

它不使用 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()了。

What a Future is, is a 我保证 to the program, you promise the program that you'll get it the value it needs sometime in the near future

如果在完成之前对它调用 .get(),那么调用它的线程将只是等待,直到完成

使用上述答案中描述的 Future 可以完成这项工作,但是像 f.get ()那样阻塞线程,直到它得到结果,这违反了并发性。

最好的解决方案是使用 Guava 的 ListenerleFuture:

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

使用这种方法,您可以在不同的时间点向 UI 线程发送多个值。您可以在这个 HandlerThread上运行(张贴)许多 Runnable对象,并且每个 Runnable都可以在 Message对象中设置值,这些值可以由 UI 线程接收。

从 Java 8开始,我们有了 CompletableFuture。 对于您的情况,您可以使用 supplyAsync方法在执行后获得结果。

Please find some reference 给你.

    CompletableFuture<Integer> completableFuture
  = CompletableFuture.supplyAsync(() -> yourMethod());


completableFuture.get() //gives you the value

这里有一个更简洁的方法,您只需要对现有代码进行一点修改。目标是从线程获得结果。结果不一定非得是 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);
}