2011-10-18 2 views
1

을 기다리고 나는 다음과 같은 상황이하십시오 알고리즘을 실행하기 위해자바 스레드 값

, 난 여러 스레드를 실행해야하고 각 스레드가 사망 직전에하는 경우 변수 x를 설정됩니다. 문제는이 스레드가 즉시 반환되지 않는다는 것입니다.

public Foo myAlgorithm() 
{ 
    //create n Runnables (n is big) 
    //start these runnables (may take long time do die) 

    //i need the x value of each runnable here, but they havent finished yet! 

    //get average x from all the runnables 

    return new Foo(averageX); 
} 

대기 알림을 사용해야합니까? 아니면 while 루프를 삽입하고 종료를 확인해야합니까?

감사합니다.

답변

4

각 스레드에서 x 값을 보유 할 수 있도록 일부 공유 저장소를 만들거나 충분하면 합계를 저장하십시오. 스레드가 종료 될 때까지 대기하려면 CountDownLatch을 사용하십시오. 작업이 끝나면 각 스레드는 CountDownLatch.countDown()을 호출하고 myAlgorithm 메서드는 CountDownLatch.await() 메서드를 사용하여 스레드를 기다립니다.

편집 : 다음은 내가 제안한 접근 방식의 전체 예입니다. 39 개의 작업자 스레드가 생성되었으며 각 스레드는 공유 합계에 임의의 숫자를 추가합니다. 모든 근로자가 끝나면 평균이 계산되고 인쇄됩니다.

import java.util.Random; 
import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.atomic.AtomicInteger; 

class Worker implements Runnable { 

    private final AtomicInteger sum; 
    private final CountDownLatch latch; 

    public Worker(AtomicInteger sum, CountDownLatch latch) { 
     this.sum = sum; 
     this.latch = latch; 
    } 

    @Override 
    public void run() { 
     Random random = new Random(); 

     try { 
      // Sleep a random length of time from 5-10s 
      Thread.sleep(random.nextInt(5000) + 5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     // Compute x 
     int x = random.nextInt(500); 

     // Add to the shared sum 
     System.out.println("Adding " + x + " to sum"); 
     sum.addAndGet(x); 

     // This runnable is finished, so count down 
     latch.countDown(); 
    } 
} 

class Program { 

    public static void main(String[] args) { 
     // There will be 39 workers 
     final int N = 39; 

     // Holds the sum of all results from all workers 
     AtomicInteger sum = new AtomicInteger(); 
     // Tracks how many workers are still working 
     CountDownLatch latch = new CountDownLatch(N); 

     System.out.println("Starting " + N + " workers"); 

     for (int i = 0; i < N; i++) { 
      // Each worker uses the shared atomic sum and countdown latch. 
      Worker worker = new Worker(sum, latch); 

      // Start the worker 
      new Thread(worker).start(); 
     } 

     try { 
      // Important: waits for all workers to finish. 
      latch.await(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     // Compute the average 
     double average = (double) sum.get()/(double) N; 

     System.out.println(" Sum: " + sum.get()); 
     System.out.println("Workers: " + N); 
     System.out.println("Average: " + average); 
    } 

} 

출력 이런 식으로 뭔가해야한다 :

Starting 39 workers 
Adding 94 to sum 
Adding 86 to sum 
Adding 454 to sum 
... 
... 
... 
Adding 358 to sum 
Adding 134 to sum 
Adding 482 to sum 
    Sum: 10133 
Workers: 39 
Average: 259.8205128205128 

편집 : 그냥 재미를 위해, 여기 ExecutorService, CallableFuture를 사용하는 예입니다.

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.List; 
import java.util.Random; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Future; 
import java.util.concurrent.ScheduledThreadPoolExecutor; 

class Worker implements Callable<Integer> { 

    @Override 
    public Integer call() throws Exception { 
     Random random = new Random(); 

     // Sleep a random length of time, from 5-10s 
     Thread.sleep(random.nextInt(5000) + 5000); 

     // Compute x 
     int x = random.nextInt(500); 
     System.out.println("Computed " + x); 

     return x; 
    } 

} 

public class Program { 

    public static void main(String[] args) { 
     // Thread pool size 
     final int POOL_SIZE = 10; 

     // There will be 39 workers 
     final int N = 39; 

     System.out.println("Starting " + N + " workers"); 

     // Create the workers 
     Collection<Callable<Integer>> workers = new ArrayList<Callable<Integer>>(N); 

     for (int i = 0; i < N; i++) { 
      workers.add(new Worker()); 
     } 

     // Create the executor service 
     ExecutorService executor = new ScheduledThreadPoolExecutor(POOL_SIZE); 

     // Execute all the workers, wait for the results 
     List<Future<Integer>> results = null; 

     try { 
      // Executes all tasks and waits for them to finish 
      results = executor.invokeAll(workers); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
      return; 
     } 

     // Compute the sum from the results 
     int sum = 0; 

     for (Future<Integer> future : results) { 
      try { 
       sum += future.get(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); return; 
      } catch (ExecutionException e) { 
       e.printStackTrace(); return; 
      } 
     } 

     // Compute the average 
     double average = (double) sum/(double) N; 

     System.out.println("   Sum: " + sum); 
     System.out.println("  Workers: " + N); 
     System.out.println("  Average: " + average); 
    } 

} 

출력은 다음과 같아야합니다

Starting 39 workers 
Computed 419 
Computed 36 
Computed 338 
... 
... 
... 
Computed 261 
Computed 354 
Computed 112 
     Sum: 9526 
    Workers: 39 
    Average: 244.25641025641025 
+0

놀라운! 나는 이것을 참고로 저장할 것입니다. 유전자 알고리즘을 코딩하여 테트리스 게임을하는 모든 에이전트는 서로 짝을 이루기 위해 게임을 마쳐야하므로 인구가 진화 할 수 있습니다. 각 게임은 다른 스레드에서 실행됩니다. 시간을내어 주시면 감사하겠습니다.이 java.util.concurrent API는 생명의 은인입니다! – Fernando

+0

한 가지 더 궁금한 점이 있습니다 : 어떤 방법으로 CountDownLatch 나 ThreadedPool이 더 빠를 것입니까? 아니면 전혀 차이가 있습니까? 감사! – Fernando

+0

제공된 예제에서 CountDownLatch가 빠릅니다. 그 이유는 CountDownLatch 예제에서 모든 스레드가 한 번에 생성되기 때문입니다. ExecutorService 예제에서, 내가 선택한'POOL_SIZE' 때문에 최대 10 개의 쓰레드가 동시에 실행될 수 있습니다. ExecutorService 예제에서'POOL_SIZE'를 39로 설정하면 결과는 CountDownLatch 예제와 거의 동일해야합니다. –

1

당신은 java.util.concurrent.Future과 ThreadPools, Executors 등과 같은 모든 관련 자료를 알 수 있습니다. 티저 : Future은 반환 값이있는 스레드입니다.

+0

* N으로 초기화 된 CountDownLatch를 사용하면 N 개의 스레드가 comp를 가질 때까지 하나의 스레드를 대기시킬 수 있습니다. 어떤 행동을했거나, 어떤 행동이 N 번 완료되었습니다. 그게 정확히 내가 필요로하는 것! 고마워요! – Fernando

0

당신이

를 제출 한 각 작업에 대한 미래를

거야 그것에 ExecutorService를 사용하고 (A Callable로) 각 작업 제출

List<Future<ResultType>> results = exec.invokeAll(tasks);//tasks is a set of Callable<ResultType> 
//invokeAll blocks untill all tasks are finished 
for(Future<ResultType> f:results){ 
    ResultType x=f.get();//loop over Futures to get the result 
    //do something with x 
} 
+0

흠집은 CountDownLatch 물건보다 단순 해 보입니다. 기대해, 고마워! – Fernando