2

Я хотел бы знать, если есть какая-то разница между этими двумя подходами выполнения задачи с использованием многопоточности в Java 7:, какой подход будет лучше - многопоточность примера

Краткого требования - Я должен обрабатывать 200 записей, одновременно, и каждая запись должна обрабатываться только один раз.

Подход 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, взятое в примере кода, будет в миллиардах в реальном сценарии. Пожалуйста, предложите, какой из них будет лучше с точки зрения памяти и процессора и т. Д.

Спасибо.

Редактировать - добавить подход 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 - миллиарды - большой прыжок. Слишком большое для нас, чтобы прокомментировать, imo. Вы должны быть более конкретными. В частности, «миллиарды» потоков не могут выполняться в отдельных системах, поэтому вам, вероятно, придется подумать о том, как распределить вашу проблему по нескольким системам. Это намного больше, чем StackOverflow был разработан для ответа. – markspace

+0

Нет, не миллиарды нитей, миллиарды записей (в 10-15 потоках). –

+0

Ооо, ну, честно говоря, я все еще не могу сказать. Мне нужно знать, что на самом деле делает ваша «задача», и немного больше об аппаратном обеспечении. Как правило, ExecutorService предпочитает создавать свои собственные потоки, если это то, о чем вы просите. – markspace

ответ

2

Краткое требование - Мне нужно обрабатывать 200 записей одновременно, и каждая запись должна обрабатываться только один раз.

Однако число 200, взятое в примере кода, будет в миллиардах в реальном сценарии.

Как всегда, ответ «это зависит». Если ваши задачи не очень быстры, накладные расходы на синхронизацию или создание объектов не должны иметь значения. Обратите внимание, что ExecutorService также должен выполнять некоторую синхронизацию.

Вы можете избежать синхронизации и упростить свой код с помощью счетчика AtomicInteger.Но я бы, скорее всего, пойти на Queue и аа пару Runnable ей poll ING как это просто и дает большую гибкость:

  • вы могли бы использовать PriorityQueue, если вы хотите, чтобы приоритеты несколько записей
  • вы можете создать свою собственную очередь, если хранить все записи в памяти было бы слишком дорого (ваша очередь может принести их из файлов или БД или любой другой)

вы можете предварительно заполнить очереди O r используйте 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 and Polling? Благодарю. –

1

Ну, я могу видеть, что в подходе 1, есть еще один объект будет обрабатываться (контер) с помощью виртуальной машины Java, и внутри счетчика есть синхронизированный блок , что Визитки стоить больше планировщика потоков, поскольку он должен будет обрабатывать Асесс переменного счетчика:

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

Так, кажется, подход-немного лучше, если мы говорим об обработке и производительности.

+2

Зависит от накладных расходов на создание нового экземпляра задачи (в случае с OPs, это в миллиардах), а не через один синхронизированный блок. Конечно, вместо использования счетчика я бы просто использовал AtomicInteger и позволил реализации Java выполнить эту работу и назвать ее днем ​​ – coffeeaddict

+0

Ничего, не думал об этом. Спасибо за улучшение перспективы. –

1

Второй подход лучше, потому что вы не синхронизируете все потоки, но я бы посоветовал вам обрабатывать более одной записи на экземпляр Task2.