스레드에서 값 반환


93

나는 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 2012

1
@JBNizet 스레드가 실제로 수행하는 작업의 복잡성을 포함하지 않았습니다. 그것은 gps 좌표를 얻고 있으므로 예, 스레드가 필요합니다.
Neeta

2
스레드의 복잡성에 관계없이 laustart하는 스레드가 시작 후 즉시 결과를 기다리면 다른 스레드를 시작하는 데 아무런 의미가 없습니다. 시작 스레드는 마치 작업 자체를 수행 한 것처럼 차단됩니다.
JB Nizet 2012

@JBNizet 무슨 뜻인지 잘 모르겠습니다 .. 다른 방식으로 설명해 주시겠습니까?
Neeta

1
스레드는 백그라운드에서 무언가를 실행할 수 있고 백그라운드 스레드가 실행되는 동안 다른 작업을 수행하는 데 사용됩니다. 스레드를 시작한 다음 스레드가 멈출 때까지 즉시 차단하면 스레드가 수행 한 작업을 직접 수행 할 수 있으며 훨씬 간단하다는 점을 제외하고는 아무런 차이가 없습니다.
JB Nizet 2012

답변:


75

지역 최종 변수 배열을 사용할 수 있습니다. 변수는 기본이 아닌 유형이어야하므로 배열을 사용할 수 있습니다. 예를 들어 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.
}

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. Blake Meike

5
CountDownLatch의 메모리 일관성 보장으로 인해 값에 대한 액세스를 위해 실제로 명시적인 동기화가 필요하지 않습니다. uiThread 시작 전에 발생하는 값 배열 생성 (프로그램 순서 규칙)은 래치 전에 발생하는 value [0] ( thread start ) 에 2 할당과 함께 발생합니다. .await () (CountDownLatch에서 보장)는 value [0] (프로그램 순서 규칙)에서 읽기 전에 발생합니다.
Adam Zalcman 2014-08-22

래치에 대해 당신이 옳은 것 같습니다! ...이 경우 실행 방법의 동기화는 쓸모가 없습니다.
G. Blake Meike 2014-08-22

좋은 지적. OP의 코드에서 복사하여 붙여 넣어야합니다. 수정되었습니다.
Adam Zalcman 2014-08-22

다음은 CountDownLatch의 예입니다. developer.android.com/reference/java/util/concurrent/…
Seagull

89

일반적으로 다음과 같이 할 것입니다.

 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().
Johan Sjöberg 2013

3
예! 잘 했어! "휘발성"ftw! 받아 들여진 대답과 달리 이것은 맞습니다!
G. Blake Meike 2014-06-14

11
@HamzahMalik 스레드가 완료되었는지 확인하려면 Thread t = new Thread(foo); t.start(); t.join(); foo.getValue();. t.join()스레드까지 블록이 완료됩니다.
Daniel

2
@HariKiran yes correct, 각 스레드는 독립적 인 값을 반환합니다.
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()완료되기 전에 호출하면 호출 하는 스레드가 완료 될 때까지 기다립니다.


제공된 코드를 두 가지로 분리했습니다. 하나는 Application 클래스에서 수행하는 풀 시작 (Android에 대해 이야기하고 있습니다)이고 두 번째로 필요한 곳에서 풀을 사용합니다. 또한 Executors.newFixedThreadPool ( 내가 하나의 작업이 서버 통화 한 번에 실행 필요에 따라 2) 나는 당신의 대답은 완벽한 @electirc 커피 덕분에 ... .. Executors.newSingleThreadExecutor ()를 사용
Gaurav Pangam

@Electric 가치를 언제 얻을 수 있는지 어떻게 알 수 있습니까? 나는 원래 포스터가 그의 방법으로이 값을 반환하고 싶었다고 믿는다. .get () 호출을 사용하면 작업이 완료된 경우에만 값을 검색합니다. 그는 블라인드 .get () 호출로 모를 것입니다.
Portfoliobuilder

4

호출 메서드의 값을 원하면 스레드가 완료 될 때까지 기다려야하므로 스레드 사용이 약간 무의미합니다.

질문에 직접 답하기 위해 호출 메서드와 스레드 모두에 대한 참조가있는 변경 가능한 개체에 값을 저장할 수 있습니다. outer를 사용할 수 this있지만 사소한 예 외에는 특별히 유용하지 않을 것입니다.

질문의 코드에 대한 약간의 참고 : 확장 Thread은 일반적으로 좋지 않은 스타일입니다. 실제로 클래스를 불필요하게 확장하는 것은 나쁜 생각입니다. 나는 당신의 run방법이 어떤 이유로 동기화되어 있음을 알았습니다 . 이제이 경우 객체 는 잠금을 사용하는 Thread모든 것을 방해 할 수 있습니다 Thread(참조 구현 join에서 IIRC와 관련이 있음).


"그런 다음 스레드가 완료 될 때까지 기다려야합니다. 스레드 사용이 약간 무의미 해집니다."
likejudo

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. 다음과 같은 HandlerUI 스레드를 만듭니다.responseHandler
  2. UI 스레드 Handler에서 초기화합니다 Looper.
  3. 에서 이에 HandlerThread대한 메시지 게시responseHandler
  4. handleMessgae도시 Toast메시지에서 수신 된 값과. 이 메시지 개체는 일반적이며 다른 유형의 속성을 보낼 수 있습니다.

이 접근 방식을 사용하면 서로 다른 시점에 여러 값을 UI 스레드에 보낼 수 있습니다. 여기에서 많은 Runnable객체를 실행 (게시) 할 수 HandlerThread있으며 각각 RunnableMessageUI 스레드에서받을 수있는 객체에 값을 설정할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.