スレッドからの戻り値


92

私は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();
}

メインスレッドがメソッドから戻る前にハンドラースレッドが完了するのを待つ必要がある場合、そもそもなぜハンドラースレッドを使用するのでしょうか。
JB Nizet

1
@JBNizetスレッドが実際に行うことの複雑さは含めませんでした。GPS座標を取得しているので、はい、スレッドが必要です。
Neeta

2
スレッドの複雑さに関係なく、laustartを実行するスレッドがスレッドの開始後すぐに結果を待つ場合、別のスレッドを開始しても意味がありません。開始スレッドは、それ自体が処理を行ったかのようにブロックされます。
JBニゼット

@JBNizetどういう意味かわかりませんが、別の方法で説明してもらえますか?
ニータ

1
スレッドは、バックグラウンドで何かを実行したり、バックグラウンドスレッドの実行中に別のことを実行したりするために使用されます。スレッドを開始し、スレッドが停止するまですぐにブロックする場合、スレッドが実行するタスクを自分で実行できますが、それがはるかに簡単になることを除いて、違いはありません。
JBニゼット

回答:


75

ローカルのfinal変数配列を使用できます。変数は非プリミティブ型である必要があるため、配列を使用できます。また、たとえば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();
}

3
いいえ。このコードは正しくありません。値へのアクセスが正しく同期されていません。
G.ブレイクメイケ2014年

5
CountDownLatchのメモリの一貫性が保証されているため、値へのアクセスに明示的な同期は実際には必要ありません。値配列の作成は、ラッチの前に発生するvalue [0](スレッド開始)への2の割り当てと同期するuiThreadの開始前に発生します(プログラム順序ルール)。ラッチの前に発生します。countDown()(プログラム順序ルール) .await()(CountDownLatchからの保証)これは、value [0]からの読み取りの前に発生します(プログラム順序ルール)。
Adam Zalcman、2014

あなたはラッチについて正しいようです!...この場合、runメソッドの同期は役に立ちません。
G.ブレイク

いい視点ね。OPのコードからコピーして貼り付けたに違いありません。修正。
Adam Zalcman、2014

ここでたCountDownLatchの例である:developer.android.com/reference/java/util/concurrent/...
シーガル

85

通常は次のようにします

 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スレッドは値を返すことができません(少なくともコールバックメカニズムがない場合は)。通常のクラスのようにスレッドを参照し、値を要求する必要があります。


2
これは本当に機能しますか?わかりますThe method getValue() is undefined for the type Thread
pmichna 2013年

1
@pmichna、良いスポッティング。からt.getValue()に変更していfoo.getValue()ます。
JohanSjöberg2013年

3
はい!よくやった!「揮発性」ftw!受け入れられた答えとは異なり、これは正しいです!
G.ブレイクメイケ2014年

11
@HamzahMalikスレッドが確実に終了するようにするには、を使用しますThread t = new Thread(foo); t.start(); t.join(); foo.getValue();t.join()スレッドが終了するまでブロックします。
ダニエル

2
@HariKiranはい正解です。各スレッドは独立した値を返します。
rootExplorr

28

探しているのは、おそらくの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();
}

8

このソリューションはどうですか?

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()完了する前に呼び出すと、それを呼び出しているスレッドは、完了するまで単に待機します。


私は提供されたコードを2つに分離しました.1つはApplicationクラスで行うプールの開始です(私はAndroidについて話している)、2つ目は必要な場所でプールを使用します... Executors.newFixedThreadPool( 2)私はExecutors.newSingleThreadExecutor()。を使用しました。サーバーの呼び出しで一度に実行する必要があるタスクは1つだけなので...回答は完璧です@electircコーヒーありがとう
Gaurav Pangam

@Electric値を取得するタイミングをどのようにして知っていますか?元のポスターは彼の方法でこの値を返したかったと思います。.get()呼び出しを使用すると値が取得されますが、操作が完了した場合のみです。彼はブラインド.get()コールでは知りませんでした
ポートフォリオ

4

呼び出し元のメソッドからの値が必要な場合は、スレッドが終了するまで待機する必要があるため、スレッドの使用は少し無意味になります。

質問に直接答えるために、値は、呼び出しメソッドとスレッドの両方が参照している任意の可変オブジェクトに格納できます。あなたはアウターを使うことができますthisが、それはささいな例以外は特に役に立ちません。

質問のコードについて少し注意してくださいThread。通常、拡張は不適切なスタイルです。実際、クラスを不必要に拡張することは悪い考えです。runメソッドが何らかの理由で同期されていることに気づきました。この場合のオブジェクトはなので、そのロックを使用Threadするものに干渉する可能性がありますThread(リファレンス実装ではjoin、IIRC で何かを行うため)。


「それでは、スレッドが終了するのを待つ必要があるため、スレッドの使用は少し無意味になります。」
likejudo 2013年

4
通常は無意味ですが、Androidではメインスレッドでサーバーにネットワークリクエストを送信できないため(アプリの応答性を維持するため)、ネットワークスレッドを使用する必要があります。アプリを再開する前に結果が必要になるシナリオがあります。
Udi Idan 2014年

2

Java 8以降には、CompletableFutureがあります。あなたのケースでは、メソッドsupplyAsyncを使用することができますを実行後に結果を取得できます。

いくつかの参考文献を見つけてください。こちらhttps://www.baeldung.com/java-completablefuture#Processing

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

   completableFuture.get() //gives you the value

1

上記の回答で説明されているFutureを使用するとうまくいきますが、f.get()と比べるとそれほど重要ではありませんが、結果が得られるまでスレッドをブロックするため、同時実行性に違反します。

最善の解決策は、Guavaの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)
        {

        }
    };

1

コードを少し変更するだけで、より一般的な方法でそれを実現できます。

 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. 次のようにHandler呼び出されるUIスレッドを作成します。responseHandler
  2. これをUIスレッドHandlerから初期化しLooperます。
  3. で、これHandlerThreadにメッセージを投稿responseHandler
  4. handleMessgaeToastメッセージから受け取ったwith値を示します。このMessageオブジェクトは汎用であり、さまざまなタイプの属性を送信できます。

このアプローチでは、さまざまな時点で複数の値をUIスレッドに送信できます。Runnableこれで多くのオブジェクトを実行(ポスト)できHandlerThread、それぞれRunnableMessageオブジェクトに値を設定できます。これは、UIスレッドで受信できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.