[java] 모든 스레드가 Java에서 작업을 마칠 때까지 기다립니다.

웹에서 동시에 정보를 가져오고 버퍼 클래스에서 5 개의 다른 필드를 채우는 5 개의 스레드가있는 애플리케이션을 작성 중입니다.
모든 스레드가 작업을 마쳤을 때 버퍼 데이터의 유효성을 검사하고 데이터베이스에 저장해야합니다.
어떻게해야합니까 (모든 스레드가 작업을 마쳤을 때 알림을 받음)?



답변

내가 취하는 접근 방식은 ExecutorService 를 사용하여 스레드 풀을 관리하는 것입니다.

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.


답변

join스레드에 할 수 있습니다 . 스레드가 완료 될 때까지 결합이 차단됩니다.

for (Thread thread : threads) {
    thread.join();
}

참고 join가 발생합니다 InterruptedException. 이런 일이 발생하면 무엇을할지 결정해야합니다 (예 : 불필요한 작업이 수행되지 않도록 다른 스레드를 취소).


답변

다양한 솔루션을 살펴보십시오.

  1. join()API는 Java의 초기 버전에 도입되었습니다. JDK 1.5 릴리스 이후이 동시 패키지에서 몇 가지 좋은 대안을 사용할 수 있습니다 .

  2. ExecutorService # invokeAll ()

    주어진 작업을 실행하고, 모든 것이 완료되면 상태와 결과를 보유한 Future 목록을 반환합니다.

    코드 예제는이 관련 SE 질문을 참조하십시오.

    모든 스레드 풀이 작업을 수행하도록 invokeAll ()을 사용하는 방법은 무엇입니까?

  3. CountDownLatch

    다른 스레드에서 수행되는 작업 집합이 완료 될 때까지 하나 이상의 스레드가 대기 할 수 있도록하는 동기화 지원입니다.

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

    사용법은이 질문을 참조하십시오. CountDownLatch

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

  4. ForkJoinPool 또는 newWorkStealingPool ()실행자

  5. 제출 후 생성 된 모든 Future 객체를 반복 합니다.ExecutorService


답변

Thread.join()다른 사람들이 제안한 것 외에도 Java 5는 실행기 프레임 워크를 도입했습니다. 거기에서 당신은 Thread물체로 작업하지 않습니다 . 대신 Callable또는 Runnable개체를 실행자 에게 제출합니다 . 여러 작업을 실행하고 그 결과를 순서에 맞지 않게 반환하는 특수 실행기가 있습니다. 그게 ExecutorCompletionService:

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

그런 다음 반환 take()Future<?>개체 가 더 이상 없을 때까지 반복적으로 호출 할 수 있으며 이는 모두 완료되었음을 의미합니다.


시나리오에 따라 관련 될 수있는 또 다른 사항은 CyclicBarrier입니다.

스레드 집합이 모두 서로가 공통 장벽 지점에 도달 할 때까지 기다릴 수있는 동기화 지원입니다. CyclicBarriers는 때때로 서로를 기다려야하는 고정 된 크기의 스레드 파티와 관련된 프로그램에서 유용합니다. 장벽은 대기중인 스레드가 해제 된 후 재사용 될 수 있기 때문에 순환이라고합니다.


답변

또 다른 가능성은 CountDownLatch간단한 상황에 유용한 객체입니다. 미리 스레드 수를 알고 있기 때문에 관련 개수로 초기화하고 객체의 참조를 각 스레드에 전달합니다.
작업이 완료되면 각 스레드 CountDownLatch.countDown()는 내부 카운터를 감소시키는 호출 을합니다. 메인 스레드는 다른 모든 것을 시작한 후 CountDownLatch.await()차단 호출을 수행해야합니다 . 내부 카운터가 0에 도달하면 바로 해제됩니다.

이 개체를 사용 InterruptedException하면를 던질 수도 있습니다.


답변

다른 스레드가 작업을 완료 할 때까지 Thread Main을 기다리거나 차단합니다.

말했듯 @Ravindra babu이 다양한 방법으로 달성 할 수 있지만 예를 들어 보여줍니다.

  • java.lang.Thread. join () 이후 : 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • java.util.concurrent.CountDownLatch 이후 : 1.5

    • .countDown() «래치 그룹의 수를 줄입니다.
    • .await() «await 메서드는 현재 카운트가 0이 될 때까지 차단됩니다.

    생성 latchGroupCount = 4한 경우 countDown()4 번 호출하여 카운트 0을 만들어야합니다. 따라서 await()차단 스레드가 해제됩니다.

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

Threaded 클래스의 예제 코드 LatchTask. 접근 방식 사용 joiningThreads();
latchThreads();주요 방법 을 테스트합니다 .

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • CyclicBarriers 스레드 집합이 서로 공통 장벽 지점에 도달 할 때까지 모두 대기 할 수 있도록하는 동기화 보조 장치 CyclicBarrier 는 때때로 서로를 기다려야하는 고정 된 크기의 스레드 파티를 포함하는 프로그램에서 유용합니다. 장벽은 대기중인 스레드가 해제 된 후 재사용 될 수 있기 때문에 순환이라고합니다.
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    

    예를 들어이 Concurrent_ParallelNotifyies 클래스 를 참조하십시오 .


  • 실행기 프레임 워크 : ExecutorService 를 사용하여 스레드 풀을 만들고 Future를 사용하여 비동기 작업의 진행 상황을 추적 할 수 있습니다 .

    • submit(Runnable), submit(Callable)Future Object를 반환합니다. future.get()함수 를 사용 하여 작업 스레드가 작업을 완료 할 때까지 주 스레드를 차단할 수 있습니다.

    • invokeAll(...) -각 Callable의 실행 결과를 얻을 수있는 Future 객체 목록을 반환합니다.

실행자 프레임 워크와 함께 실행 가능한 인터페이스, 호출 가능한 인터페이스를 사용하는 예제찾으십시오 .


@또한보십시오


답변

당신은

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

이 for 루프 후에 모든 스레드가 작업을 완료했는지 확인할 수 있습니다.