웹에서 동시에 정보를 가져오고 버퍼 클래스에서 5 개의 다른 필드를 채우는 5 개의 스레드가있는 애플리케이션을 작성 중입니다.
모든 스레드가 작업을 마쳤을 때 버퍼 데이터의 유효성을 검사하고 데이터베이스에 저장해야합니다.
어떻게해야합니까 (모든 스레드가 작업을 마쳤을 때 알림을 받음)?
웹에서 동시에 정보를 가져오고 버퍼 클래스에서 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.
while(!es.awaitTermination(1, TimeUnit.MINUTES));
es.shutdown();
해야합니까? 무엇을 내가 사용하여 스레드를 실행하는 코드를 작성하는 경우 es.execute(runnableObj_ZipMaking);
에 try
블록에 finally
내가 전화를 boolean finshed = es.awaitTermination(10, TimeUnit.MINUTES);
. 따라서 모든 스레드가 작업을 완료하거나 시간 초과가 발생할 때까지 기다려야한다고 생각합니다 (먼저 무엇이든), 내 가정이 맞습니까? 또는 전화 shutdown()
는 필수입니까?
join
스레드에 할 수 있습니다 . 스레드가 완료 될 때까지 결합이 차단됩니다.
for (Thread thread : threads) {
thread.join();
}
참고 join
가 발생합니다 InterruptedException
. 이런 일이 발생하면 무엇을할지 결정해야합니다 (예 : 불필요한 작업이 수행되지 않도록 다른 스레드를 취소).
t.join();
의미합니다 . 스레드에는 영향을주지 않습니다 . t
t
다양한 솔루션을 살펴보십시오.
join()
API는 Java의 초기 버전에 도입되었습니다. JDK 1.5 릴리스 이후이 동시 패키지에서 몇 가지 좋은 대안을 사용할 수 있습니다 .
ExecutorService # invokeAll ()
주어진 작업을 실행하고, 모든 것이 완료되면 상태와 결과를 보유한 Future 목록을 반환합니다.
코드 예제는이 관련 SE 질문을 참조하십시오.
다른 스레드에서 수행되는 작업 집합이 완료 될 때까지 하나 이상의 스레드가 대기 할 수 있도록하는 동기화 지원입니다.
해, CountDownLatch는 주어진 카운트 초기화됩니다. await 메서드는
countDown()
메서드 호출로 인해 현재 카운트가 0에 도달 할 때까지 차단되며 , 그 후 모든 대기 스레드가 해제되고 이후의 모든 await 호출이 즉시 반환됩니다. 이것은 원샷 현상으로 카운트를 재설정 할 수 없습니다. 카운트를 재설정하는 버전이 필요한 경우 CyclicBarrier 사용을 고려하십시오 .
사용법은이 질문을 참조하십시오. CountDownLatch
제출 후 생성 된 모든 Future 객체를 반복 합니다.ExecutorService
Thread.join()
다른 사람들이 제안한 것 외에도 Java 5는 실행기 프레임 워크를 도입했습니다. 거기에서 당신은 Thread
물체로 작업하지 않습니다 . 대신 Callable
또는 Runnable
개체를 실행자 에게 제출합니다 . 여러 작업을 실행하고 그 결과를 순서에 맞지 않게 반환하는 특수 실행기가 있습니다. 그게 ExecutorCompletionService
:
ExecutorCompletionService executor;
for (..) {
executor.submit(Executors.callable(yourRunnable));
}
그런 다음 반환 take()
할 Future<?>
개체 가 더 이상 없을 때까지 반복적으로 호출 할 수 있으며 이는 모두 완료되었음을 의미합니다.
시나리오에 따라 관련 될 수있는 또 다른 사항은 CyclicBarrier
입니다.
스레드 집합이 모두 서로가 공통 장벽 지점에 도달 할 때까지 기다릴 수있는 동기화 지원입니다. CyclicBarriers는 때때로 서로를 기다려야하는 고정 된 크기의 스레드 파티와 관련된 프로그램에서 유용합니다. 장벽은 대기중인 스레드가 해제 된 후 재사용 될 수 있기 때문에 순환이라고합니다.
executor.submit
를 반환합니다 Future<?>
. 이 선물을 목록에 추가 한 다음 각 선물을 호출하는 목록을 반복합니다 get
.
Executors
. 예 : Executors.newCachedThreadPool
(또는 유사)
또 다른 가능성은 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();
}
}
CyclicBarrier barrier = new CyclicBarrier(3);
barrier.await();
예를 들어이 Concurrent_ParallelNotifyies 클래스 를 참조하십시오 .실행기 프레임 워크 : ExecutorService 를 사용하여 스레드 풀을 만들고 Future를 사용하여 비동기 작업의 진행 상황을 추적 할 수 있습니다 .
submit(Runnable)
, submit(Callable)
Future Object를 반환합니다. future.get()
함수 를 사용 하여 작업 스레드가 작업을 완료 할 때까지 주 스레드를 차단할 수 있습니다.
invokeAll(...)
-각 Callable의 실행 결과를 얻을 수있는 Future 객체 목록을 반환합니다.
실행자 프레임 워크와 함께 실행 가능한 인터페이스, 호출 가능한 인터페이스를 사용하는 예제 를 찾으십시오 .
@또한보십시오
이를 위해 Threadf # join 메소드를 사용할 수 있습니다 .
OP의 문제와는 관련이 없지만 정확히 하나의 스레드와 동기화 (보다 정확하게는 랑데부)에 관심이 있다면 Exchanger를 사용할 수 있습니다.
제 경우에는 자식 스레드가 초기화를 완료 할 때까지 부모 스레드를 일시 중지해야했습니다. CountDownLatch를이 또한 잘 작동합니다.
실행기 서비스를 사용하여 상태 및 완료를 포함하여 여러 스레드를 관리 할 수 있습니다. http://programmingexamples.wikidot.com/executorservice 참조
이것을 시도하면 작동합니다.
Thread[] threads = new Thread[10];
List<Thread> allThreads = new ArrayList<Thread>();
for(Thread thread : threads){
if(null != thread){
if(thread.isAlive()){
allThreads.add(thread);
}
}
}
while(!allThreads.isEmpty()){
Iterator<Thread> ite = allThreads.iterator();
while(ite.hasNext()){
Thread thread = ite.next();
if(!thread.isAlive()){
ite.remove();
}
}
}
비슷한 문제가있어서 Java 8 parallelStream을 사용하게되었습니다.
requestList.parallelStream().forEach(req -> makeRequest(req));
매우 간단하고 읽기 쉽습니다. 이면에서는 기본 JVM의 포크 조인 풀을 사용합니다. 즉, 계속하기 전에 모든 스레드가 완료 될 때까지 기다릴 것입니다. 내 경우에는 내 응용 프로그램에서 유일한 parallelStream 이었기 때문에 깔끔한 솔루션이었습니다. 하나 이상의 parallelStream이 동시에 실행되는 경우 아래 링크를 읽으십시오.
병렬 스트림에 대한 자세한 정보는 여기를 참조하십시오 .
기존 답변은 join()
각 스레드 수 있습니다.
그러나 스레드 배열 / 목록을 얻는 방법에는 여러 가지가 있습니다.
ThreadGroup
스레드를 관리하는 데 사용 합니다.다음 코드는 ThreadGruop
접근 방식 을 사용합니다 . 먼저 그룹을 만든 다음 각 스레드를 만들 때 생성자에서 그룹을 지정하고 나중에 스레드 배열을ThreadGroup.enumerate()
SyncBlockLearn.java
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* synchronized block - learn,
*
* @author eric
* @date Apr 20, 2015 1:37:11 PM
*/
public class SyncBlockLearn {
private static final int TD_COUNT = 5; // thread count
private static final int ROUND_PER_THREAD = 100; // round for each thread,
private static final long INC_DELAY = 10; // delay of each increase,
// sync block test,
@Test
public void syncBlockTest() throws InterruptedException {
Counter ct = new Counter();
ThreadGroup tg = new ThreadGroup("runner");
for (int i = 0; i < TD_COUNT; i++) {
new Thread(tg, ct, "t-" + i).start();
}
Thread[] tArr = new Thread[TD_COUNT];
tg.enumerate(tArr); // get threads,
// wait all runner to finish,
for (Thread t : tArr) {
t.join();
}
System.out.printf("\nfinal count: %d\n", ct.getCount());
Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
}
static class Counter implements Runnable {
private final Object lkOn = new Object(); // the object to lock on,
private int count = 0;
@Override
public void run() {
System.out.printf("[%s] begin\n", Thread.currentThread().getName());
for (int i = 0; i < ROUND_PER_THREAD; i++) {
synchronized (lkOn) {
System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
}
try {
Thread.sleep(INC_DELAY); // wait a while,
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf("[%s] end\n", Thread.currentThread().getName());
}
public int getCount() {
return count;
}
}
}
주 스레드는 그룹의 모든 스레드가 완료 될 때까지 기다립니다.
메인 스레드에서 이것을 사용하십시오 : while (! executor.isTerminated ()); 실행기 서비스에서 모든 스레드를 시작한 후에이 코드 줄을 넣으십시오. 실행자가 시작한 모든 스레드가 완료된 후에 만 주 스레드를 시작합니다. executor.shutdown ();을 호출하십시오. 위의 루프 전에.