[java] 다른 스레드가 완료되었는지 확인하는 방법은 무엇입니까?

StartDownload()세 개의 스레드를 시작 하는라는 메서드가있는 객체가 있습니다.

각 스레드의 실행이 완료되면 알림을 받으려면 어떻게합니까?

스레드 중 하나 (또는 ​​모두)가 완료되었거나 여전히 실행 중인지 알 수있는 방법이 있습니까?



답변

이를 수행 할 수있는 여러 가지 방법이 있습니다.

  1. 사용 를 Thread.join () 주 스레드에서 각 스레드가 완료 될 때까지 차단 방식으로 기다리거나하는
  2. 각 스레드가 완료 될 때까지 기다리려면 폴링 방식으로 Thread.isAlive () 를 확인하십시오 ( 일반적으로 권장하지 않음).
  3. Unorthodox는 문제의 각 스레드에 대해 setUncaughtExceptionHandler 를 호출하여 객체의 메소드를 호출하고 각 스레드가 완료 될 때 포착되지 않은 예외를 처리하도록 프로그래밍합니다.
  4. 에서 잠금 또는 싱크로 나이저 또는 메커니즘을 사용 하고있는 java.util.concurrent , 또는
  5. 더 많은 정통, 기본 스레드에서 리스너를 만든 다음 각 스레드를 프로그래밍하여 리스너에게 완료되었음을 알리십시오.

아이디어 # 5를 구현하는 방법? 한 가지 방법은 먼저 인터페이스를 만드는 것입니다.

public interface ThreadCompleteListener {
    void notifyOfThreadComplete(final Thread thread);
}

그런 다음 다음 클래스를 만듭니다.

public abstract class NotifyingThread extends Thread {
  private final Set<ThreadCompleteListener> listeners
                   = new CopyOnWriteArraySet<ThreadCompleteListener>();
  public final void addListener(final ThreadCompleteListener listener) {
    listeners.add(listener);
  }
  public final void removeListener(final ThreadCompleteListener listener) {
    listeners.remove(listener);
  }
  private final void notifyListeners() {
    for (ThreadCompleteListener listener : listeners) {
      listener.notifyOfThreadComplete(this);
    }
  }
  @Override
  public final void run() {
    try {
      doRun();
    } finally {
      notifyListeners();
    }
  }
  public abstract void doRun();
}

그런 다음 각 스레드가 확장 NotifyingThread되고 구현 run()하는 대신 구현 doRun()됩니다. 따라서 완료되면 알림을 기다리는 사람에게 자동으로 알립니다.

마지막으로, 모든 스레드를 시작하는 클래스 (또는 적어도 알림을 기다리는 객체)의 기본 클래스에서 implement ThreadCompleteListener각 스레드를 생성 한 직후와 즉시 해당 클래스를 리스너 목록에 추가하십시오.

NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start();           // Start the Thread

그런 다음 각 스레드가 종료되면 notifyOfThreadComplete방금 완료되었거나 충돌 한 Thread 인스턴스와 함께 메서드가 호출됩니다.

더 잘 참고하는 것 implements Runnable보다는 extends Thread위한 NotifyingThread스레드를 확장하는 것은 일반적으로 새로운 코드에 낙심 될 때. 그러나 나는 당신의 질문에 코딩하고 있습니다. NotifyingThread구현 하도록 클래스를 변경하면 Runnable스레드를 관리하는 코드 중 일부를 변경해야합니다. 이는 매우 간단합니다.


답변

CyclicBarrier를 사용한 솔루션

public class Downloader {
  private CyclicBarrier barrier;
  private final static int NUMBER_OF_DOWNLOADING_THREADS;

  private DownloadingThread extends Thread {
    private final String url;
    public DownloadingThread(String url) {
      super();
      this.url = url;
    }
    @Override
    public void run() {
      barrier.await(); // label1
      download(url);
      barrier.await(); // label2
    }
  }
  public void startDownload() {
    // plus one for the main thread of execution
    barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0
    for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) {
      new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start();
    }
    barrier.await(); // label3
    displayMessage("Please wait...");
    barrier.await(); // label4
    displayMessage("Finished");
  }
}

label0- 주기적 스레드는 실행 스레드 수에 더한 당사자 수와 기본 실행 스레드 하나 (startDownload ()가 실행되는 스레드 수)로 생성됩니다.

라벨 1 -n 번째 다운로드 스레드가 대기실에 들어갑니다.

라벨 3 -NUMBER_OF_DOWNLOADING_THREADS가 대기실에 입장했습니다. 주요 실행 스레드는 거의 동시에 다운로드 작업을 시작하기 위해 릴리스합니다.

레이블 4- 주요 실행 스레드가 대기실에 들어갑니다. 이것은 이해하기 쉬운 코드 중 가장 까다로운 부분입니다. 대기실에 두 번째 스레드가 들어가는 것은 중요하지 않습니다. 실에 들어가는 스레드가 무엇이든 다른 모든 다운로드 스레드가 다운로드 작업을 완료했는지 확인하는 것이 중요합니다.

라벨 2 -n 번째 다운로드 중 스레드가 다운로드 작업을 마치고 대기실로 들어갑니다. 그것이 메인 실행 스레드를 포함하여 이미 NUMBER_OF_DOWNLOADING_THREADS에 들어간 마지막 스레드 인 경우 메인 스레드는 다른 모든 스레드가 다운로드를 완료 한 경우에만 실행을 계속합니다.


답변

당신은해야 정말 사용하는 솔루션을 선호합니다 java.util.concurrent. 주제에서 Josh Bloch 및 / 또는 Brian Goetz를 찾아 읽으십시오.

java.util.concurrent.*쓰레드를 직접 사용 하지 않고 직접 사용할 책임 이 있다면 join(), 쓰레드가 언제 완료되는지 알아야한다. 다음은 매우 간단한 콜백 메커니즘입니다. 먼저 Runnable인터페이스를 확장하여 콜백을 갖습니다.

public interface CallbackRunnable extends Runnable {
    public void callback();
}

그런 다음 런너 블을 실행할 실행자를 만들고 완료되면 다시 전화하십시오.

public class CallbackExecutor implements Executor {

    @Override
    public void execute(final Runnable r) {
        final Thread runner = new Thread(r);
        runner.start();
        if ( r instanceof CallbackRunnable ) {
            // create a thread to perform the callback
            Thread callerbacker = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // block until the running thread is done
                        runner.join();
                        ((CallbackRunnable)r).callback();
                    }
                    catch ( InterruptedException e ) {
                        // someone doesn't want us running. ok, maybe we give up.
                    }
                }
            });
            callerbacker.start();
        }
    }

}

CallbackRunnable인터페이스 에 추가해야 할 또 다른 확실한 것은 예외를 처리하는 수단이므로, public void uncaughtException(Throwable e);거기에 행을 넣고 실행기에 Thread.UncaughtExceptionHandler를 설치하여 해당 인터페이스 메소드로 보내십시오.

그러나 실제로 모든 냄새를 맡기 시작합니다 java.util.concurrent.Callable. java.util.concurrent프로젝트에서 허용하는 경우 실제로 사용을 살펴 봐야 합니다.


답변

그들이 끝날 때까지 기다리시겠습니까? 그렇다면 Join 메서드를 사용하십시오.

확인하려는 경우 isAlive 속성도 있습니다.


답변

getState ()로 스레드 인스턴스를 조사하여 다음 값 중 하나를 사용하여 Thread.State 열거의 인스턴스를 리턴합니다.

*  NEW
  A thread that has not yet started is in this state.
* RUNNABLE
  A thread executing in the Java virtual machine is in this state.
* BLOCKED
  A thread that is blocked waiting for a monitor lock is in this state.
* WAITING
  A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING
  A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED
  A thread that has exited is in this state.

그러나 3 명의 자식이 완료되기를 기다리는 마스터 스레드를 갖는 것이 더 나은 디자인이라고 생각하고 다른 3 명이 완료되면 마스터는 계속 실행합니다.


답변

Executors개체를 사용하여 ExecutorService 스레드 풀 을 만들 수도 있습니다. 그런 다음이 invokeAll방법을 사용하여 각 스레드를 실행하고 선물을 검색하십시오. 모든 실행이 완료 될 때까지 차단됩니다. 다른 옵션은 풀을 사용하여 각각을 실행 한 다음 awaitTermination풀 실행이 완료 될 때까지 블록 을 호출 하는 것입니다. shutdown작업 추가가 완료되면 () 로 전화하십시오 .


답변

멀티 스레딩 프론트에서 지난 6 년 동안 많은 것들이 변경되었습니다.

join()API 를 사용 하고 잠그는 대신 사용할 수 있습니다.

1. ExecutorService invokeAll() API

주어진 작업을 실행하고 모든 완료시 상태 및 결과를 보유한 선물리스트를 반환합니다.

2. CountDownLatch

하나 이상의 스레드가 다른 스레드에서 수행중인 작업 세트가 완료 될 때까지 대기 할 수 있도록하는 동기화 지원.

CountDownLatch주어진 카운트로 A 가 초기화됩니다. await 메소드는 countDown()메소드 호출로 인해 현재 카운트가 0에 도달 할 때까지 차단되며 , 그 후에 모든 대기 스레드가 해제되고 이후의 await 호출이 즉시 리턴됩니다. 이것은 일회성 현상이므로 카운트를 재설정 할 수 없습니다. 카운트를 재설정하는 버전이 필요한 경우 CyclicBarrier 사용을 고려하십시오.

3. ForkJoinPool 또는 newWorkStealingPool()에서 집행 인은 다른 방법입니다

Future제출에서 모든 작업을 반복 하고 객체 에 대한 ExecutorService호출 get()을 차단하여 상태를 확인하십시오.Future

관련 SE 질문을 살펴보십시오.

자체 스레드를 생성하는 스레드를 기다리는 방법은 무엇입니까?

실행자 : 작업이 재귀 적으로 생성되면 모든 작업이 완료 될 때까지 동기식으로 대기하는 방법은 무엇입니까?