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();
}
ローカルの最終変数配列を使用できます。変数は非プリミティブ型である必要があるため、配列を使用できます。また、たとえば CountDownLatch を使用して、2つのスレッドを同期する必要があります。
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.
}
Executor
と Callable
を次のように使用することもできます。
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();
}
通常、あなたはこのようなことをするでしょう
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
スレッドは値を返すことができません(少なくともコールバックメカニズムがなければ)。通常のクラスのようにスレッドを参照し、値を要求する必要があります。
あなたが探しているのは、おそらくRunnable
の代わりにCallable<V>
インターフェースであり、Future<V>
オブジェクトで値を取得し、値が計算されるまで待つことです。これはExecutors.newSingleThreadExecutor()
から取得できるExecutorService
で実現できます。
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();
}
このソリューションはどうですか?
Threadクラスは使用しませんが、ISコンカレントであり、要求どおりに機能します
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()
を呼び出すと、呼び出し元のスレッドは単に完了するまで待機します。
呼び出し元のメソッドからの値が必要な場合は、スレッドが終了するまで待機する必要があります。これにより、スレッドの使用が無意味になります。
質問に直接答えるために、値は呼び出し元のメソッドとスレッドの両方が参照を持つ任意の可変オブジェクトに格納できます。外側のthis
を使用することもできますが、些細な例以外には特に役立ちません。
質問のコードに関するちょっとした注意:Thread
を拡張することは、通常スタイルが悪いです。実際、不必要にクラスを拡張することは悪い考えです。 run
メソッドが何らかの理由で同期されていることに気付きました。この場合のオブジェクトはThread
であるため、ロックを使用するThread
に干渉する可能性があります(参照実装では、join
、IIRCに関連するもの)。
Java 8はCompletableFutureを提供します。これがワンストップソリューションになります。 http://www.baeldung.com/Java-completablefuture
上記の回答で説明されているFutureを使用するとジョブが実行されますが、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)
{
}
};
コードを少し変更するだけで、より一般的な方法でコードを実現できます。
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();
解決 :
Handler
と呼ばれるresponseHandler
を作成しますHandler
からこのLooper
を初期化します。HandlerThread
で、このresponseHandler
にメッセージを投稿しますhandleMessgae
は、メッセージから受信した値を持つToast
を示します。このMessageオブジェクトは汎用であり、異なるタイプの属性を送信できます。このアプローチを使用すると、異なる時点で複数の値をUIスレッドに送信できます。このRunnable
で多くのHandlerThread
オブジェクトを実行(ポスト)でき、各Runnable
はMessage
オブジェクトに値を設定でき、UIスレッドで受信できます。