Returning value from Thread
JavaAndroidMultithreadingJava Problem Overview
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();
}
Java Solutions
Solution 1 - Java
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();
Thread thread = new Thread(foo);
thread.start();
thread.join();
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.
Solution 2 - Java
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();
}
Solution 3 - Java
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();
}
Solution 4 - Java
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
Solution 5 - Java
From Java 8 onwards we have CompletableFuture
.
On your case, you may use the method supplyAsync
to get the result after execution.
Please find some reference here.
CompletableFuture<Integer> completableFuture
= CompletableFuture.supplyAsync(() -> yourMethod());
completableFuture.get() //gives you the value
Solution 6 - Java
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).
Solution 7 - Java
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)
{
}
};
Solution 8 - Java
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 asresponseHandler
- Initialize this
Handler
fromLooper
of UI Thread. - In
HandlerThread
, post message on thisresponseHandler
handleMessgae
shows aToast
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.
Solution 9 - Java
Here is a cleaner approach, you just need a bit change to your existing code. The goal is to get the result from the the Thread. It doesn't really have to be return
a result. Instead, using a callback style to take that result and do further processing.
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);
}