可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a method with a HandlerThread
. A value gets changed inside the Thread
and I\'d like to return it to the test()
method. Is there a way to do this?
public void test()
{
Thread uiThread = new HandlerThread(\"UIHandler\"){
public synchronized void run(){
int value;
value = 2; //To be returned to test()
}
};
uiThread.start();
}
回答1:
You can use a local final variable array. The variable needs to be of non-primitive type, so you can use an array. You also need to synchronize the two threads, for example using a 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.
}
You can also use an Executor
and a Callable
like this:
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:
Usually you would do it something like this
public class Foo implements Runnable {
private volatile int value;
@Override
public void run() {
value = 2;
}
public int getValue() {
return value;
}
}
Then you can create the thread and retrieve the value (given that the value has been set)
Foo foo = new Foo();
new Thread(foo).start();
// ... join through some method
int value = foo.getValue();
tl;dr
a thread cannot return a value (at least not without a callback mechanism). You should reference a thread like an ordinary class and ask for the value.
回答3:
What you are looking for is probably the Callable<V>
interface in place of Runnable
, and retrieving the value with a Future<V>
object, which also lets you wait until the value has been computed. You can achieve this with an ExecutorService
, which you can get from 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:
How about this solution?
It doesn\'t use the Thread class, but it IS concurrent, and in a way it does exactly what you request
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;}
});
Now all you do is say value.get()
whenever you need to grab your returned value, the thread is started the very second you give value
a value so you don\'t ever have to say threadName.start()
on it.
What a Future
is, is a promise to the program, you promise the program that you\'ll get it the value it needs sometime in the near future
If you call .get()
on it before it\'s done, the thread that\'s calling it will simply just wait until it\'s done
回答5:
If you want the value from the calling method, then it should wait for the thread to finish, which makes using threads a bit pointless.
To directly answer you question, the value can be stored in any mutable object both the calling method and the thread both have a reference to. You could use the outer this
, but that isn\'t going to be particularly useful other than for trivial examples.
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).
回答6:
Using Future described in above answers does the job, but a bit less significantly as f.get(), blocks the thread until it gets the result, which violates concurrency.
Best solution is to use Guava\'s ListenableFuture. An example :
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:
With small modifications to your code, you can achieve it in a more generic way.
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();
Solution :
- Create a
Handler
in UI Thread,which is called as responseHandler
- Initialize this
Handler
from Looper
of UI Thread.
- In
HandlerThread
, post message on this responseHandler
handleMessgae
shows a Toast
with value received from message. This Message object is generic and you can send different type of attributes.
With this approach, you can send multiple values to UI thread at different point of times. You can run (post) many Runnable
objects on this HandlerThread
and each Runnable
can set value in Message
object, which can be received by UI Thread.
回答8:
Java 8 provides CompletableFuture . it will be the one stop solution for this.
http://www.baeldung.com/java-completablefuture