2

멀티 스레딩 동시에 200 개의 레코드가 있어야하며 각 레코드는 한 번만 처리해야합니다.는 더 나은 방법이 될 나는 자바 7에 멀티 스레딩을 사용하여 작업을 수행하는 이러한 두 가지 접근 방식의 차이가 있는지 알고 싶습니다 예를

접근 1 :

public class Counter { 

    private static int count = -1; 
    private static final Object lock = new Object(); 

    public static int getCount() { 
     synchronized (lock) { 
      return ++count; 
     } 
    } 
} 



public class Task implements Runnable { 

    @Override 
    public void run() { 
     int count = 0; 
     while((count = Counter.getCount()) < 200) { 
      System.out.println(Thread.currentThread().getName() + " " + count); 
      // process record # count 
     } 
    } 
} 

public class Tester { 

    public static void main(String[] args) throws InterruptedException { 
     long start = System.currentTimeMillis(); 
     ExecutorService service = Executors.newFixedThreadPool(10); 
     for (int i = 0; i < 10; i++) { 
      service.execute(new Task()); 
     } 
     service.shutdown(); 
     service.awaitTermination(5, TimeUnit.MINUTES); 
     long end = System.currentTimeMillis(); 
     System.out.println(end - start); 
    } 
} 

접근법 2 : 둘 다 잘 실행과 같은 출력을 생성 거의 같은 시간이 소요

public class Task2 implements Runnable { 

    private int i = 0; 

    public Task2(int i) { 
     super(); 
     this.i = i; 
    } 

    @Override 
    public void run() { 
     System.out.println(Thread.currentThread().getName() + " " + i); 
     // process record # i 
    } 
} 

public class Tester2 { 

    public static void main(String[] args) throws InterruptedException { 
     long start = System.currentTimeMillis(); 
     ExecutorService service = Executors.newFixedThreadPool(10); 
     for (int i = 0; i < 200; i++) { 
      service.execute(new Task2(i)); 
     } 
     service.shutdown(); 
     service.awaitTermination(5, TimeUnit.MINUTES); 
     long end = System.currentTimeMillis(); 
     System.out.println(end - start); 
    } 
} 

. 그러나 예제 코드에서 취한 200 개의 숫자는 실제 시나리오에서 수십억입니다. 어느 것이 메모리 및 CPU 등의면에서 더 좋을지 제안하십시오.

감사합니다.

편집 - 지미 T.의 제안에 따라 접근 방식 3 추가.

접근법 3 :

public class Tester3 { 

    public static void main(String[] args) throws InterruptedException { 

     int processedRecords = 0; 
     int totalRecords = 200; 
     int recordsPerThread = 10; 
     boolean continueProcess = true; 

     ExecutorService service = Executors.newFixedThreadPool(10); 

     while(continueProcess) { 
      int startIndex = processedRecords; 
      int endIndex = startIndex + recordsPerThread - 1; 
      if (endIndex >= totalRecords - 1) { 
       endIndex = totalRecords - 1; 
       continueProcess = false; 
      } 
      processedRecords = processedRecords + recordsPerThread; 
      service.submit(new Task3(startIndex, endIndex)); 
     } 
     service.shutdown(); 
     service.awaitTermination(5, TimeUnit.MINUTES); 
    } 
} 


public class Task3 implements Runnable { 

    private int startIndex = 0; 
    private int endIndex = 0; 

    public Task3(int startIndex, int endIndex) { 
     super(); 
     this.startIndex = startIndex; 
     this.endIndex = endIndex; 
    } 

    @Override 
    public void run() { 
     System.out.println("processing records from " + startIndex + " to " + endIndex); 
    } 
} 
+2

200 억은 큰 도약입니다. 우리가 의견을 말하기에는 너무 큽니다. 좀 더 구체적이어야합니다. 특히 수십억 개의 스레드를 단일 시스템에서 실행할 수 없으므로 여러 시스템에 문제를 분산시키는 방법을 생각해야 할 것입니다. StackOverflow가 대답하도록 고안된 것 이상으로 좋습니다. – markspace

+0

수십억 개의 스레드가 아니라 수십억 개의 레코드 (10-15 스레드). –

+0

우우, 솔직히 나는 아직도 말할 수 없다. 당신의 "작업"이 실제로하는 일과 하드웨어에 대해서 좀 더 알아야합니다. 일반적으로 ExecutorService가 자신의 스레드를 만드는 것보다 선호됩니다. – markspace

답변

2

간략한 요구 사항 - 동시에 200 개의 레코드를 처리해야하며 각 레코드는 한 번만 처리해야합니다.

그러나 예제 코드에서 취한 200 숫자는 실제 시나리오에서 수십억입니다.

언제나처럼 대답은 "의존적"입니다. 작업이 매우 빠르지 않으면 동기화 또는 객체 생성의 오버 헤드가 중요하지 않아야합니다. ExecutorService은 내부적으로 동기화를 수행해야합니다.

AtomicInteger 카운터를 사용하면 동기화를 피하고 코드를 단순화 할 수 있습니다.하지만 대부분의 아마 그것은 간단하고 당신에게 많은 유연성 제공으로 보내고 RunnablepollQueue 및 AA 몇 가고 싶어 : 당신은 몇 가지 기록에 우선 순위를 원한다면

  • 당신이 PriorityQueue을 사용할 수를
  • 메모리에 모든 레코드를 저장하는 비용이 너무 많이한다면 당신이 당신의 자신의 대기열을 만들 수 있습니다 당신은 당신의 큐 O를 미리 채울 수

(대기열 파일 또는 DB 또는 어떤에서 그들을 가져올 수) 제작자가 채운 BlockingQueue을 사용하십시오. 대기열의 내용은 레코드 색인 일 수도 있고 레코드 자체 일 수도 있습니다.

모든 작업은 대기열을 폴링하고 비어있을 때 종료합니다.

public class Task3 implements Runnable { 
    @Override public void run() { 
     while (true) { 
      final Integer i = queue.poll(); 
      if (i==null) break;  
      System.out.println(Thread.currentThread().getName() + " " + i); 
      // process record # i 
     } 
    } 
} 

코어 수를 늘리려면 제한된 수의 수를 만듭니다.

ExecutorService service = Executors.newFixedThreadPool(10); 
for (int i = 0; i < 10; i++) service.execute(new Task3()); 
+0

아마도 제한된 BlockingQueue를 사용하고 용량이 가득 차면 제작자가 대기하게 할 것입니다. 나는 소비자를 계속 추가하거나 더 큰 규모로 확장 할 수 있기 때문에 큐 솔루션을 좋아한다. (다중 서버가 JMS 큐를 치는 것처럼) – coffeeaddict

+0

고마워. 작업은 실제로 매우 빨라질 것입니다. –

+0

안녕하세요 maaartinus, AtomicInteger 제안에 감사드립니다. Queue, Runnable 및 Polling 솔루션에 대한 자세한 내용을 제공해 주시겠습니까? 감사. –

1

음이 난 보시 모든 접근법 1에있어서, JVM 그래피 (Conter) 처리 할 하나 이상의 객체가, 카운터 내부의 동기 블록이 있다는 ,가는 요리가 변수 개수에 ACESS을 처리해야하기 때문에, 스레드 스케줄러 더 많은 비용이 있음 :

public static int getCount() { 
     synchronized (lock) { 
      return ++count; 
     } 
    } 

그래서, 접근 방식이 우리가 처리 및 성능에 대해 이야기하면 조금 더 나은 것 같다.

+2

새 작업 인스턴스를 만드는 오버 헤드 (OPs의 경우 이것은 수십억에 달려 있음)에 따라 한 라이너 동기화 블록을 통과하는 것입니다. 물론 Counter를 사용하는 대신 AtomicInteger를 사용하여 Java 구현이 작업을 수행하고 하루 만 호출하도록하십시오. – coffeeaddict

+0

Nice에 대해 생각하지 않았습니다. 관점을 개선해 주셔서 감사합니다 –

1

두 번째 방법은 모든 스레드를 동기화하지 않지만 Task2 인스턴스 당 둘 이상의 레코드를 처리하도록 권장하기 때문에 더 좋습니다.