web-dev-qa-db-ja.com

スレッドから値を返す

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();
}
82
Neeta

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

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();
}
68
Adam Zalcman

通常、あなたはこのようなことをするでしょう

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

66
Johan Sjöberg

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

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

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

6
Electric Coffee

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

質問に直接答えるために、値は呼び出し元のメソッドとスレッドの両方が参照を持つ任意の可変オブジェクトに格納できます。外側のthisを使用することもできますが、些細な例以外には特に役立ちません。

質問のコードに関するちょっとした注意:Threadを拡張することは、通常スタイルが悪いです。実際、不必要にクラスを拡張することは悪い考えです。 runメソッドが何らかの理由で同期されていることに気付きました。この場合のオブジェクトはThreadであるため、ロックを使用するThreadに干渉する可能性があります(参照実装では、join、IIRCに関連するもの)。

Java 8はCompletableFutureを提供します。これがワンストップソリューションになります。 http://www.baeldung.com/Java-completablefuture

1
Vinto

上記の回答で説明されている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)
        {

        }
    };
1
bitsabhi

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

 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スレッドでHandlerと呼ばれるresponseHandlerを作成します
  2. UIスレッドのHandlerからこのLooperを初期化します。
  3. HandlerThreadで、このresponseHandlerにメッセージを投稿します
  4. handleMessgaeは、メッセージから受信した値を持つToastを示します。このMessageオブジェクトは汎用であり、異なるタイプの属性を送信できます。

このアプローチを使用すると、異なる時点で複数の値をUIスレッドに送信できます。このRunnableで多くのHandlerThreadオブジェクトを実行(ポスト)でき、各RunnableMessageオブジェクトに値を設定でき、UIスレッドで受信できます。

0
Ravindra babu