Многопоточность Java не использует все ядра

У меня есть многопоточная программа, которая, похоже, не использует все ядра на моей машине. Вот код, и любой вклад будет высоко оценен.

Основной класс

public class MainClass{
 public static void main(String [] args){
  Work work=new Work();
  work.doIt();
 }
}

Второй класс создает задачи и передает их ExecutorService, вот псевдокод

public class Work{
 public void doIt() throws InterrputedException, Exception{
  map=get some data and put it in the map;
  ArrayList<Future<Integer>> list=new ArrayList<Future<Integer>>();
  ArrayList<WorkCallable>jobs=new ArrayList<WorkCallable>();
  for each entry in the map;
    jobs.add(new WorkCallable(entry);
  int numCores=Runtime.getRuntime().availableProcessors();
  ExecutorService executor=Executors.newFixedThreadPool(numCores);
  int size=jobs.size();
  for(int i=0;i<size;i++){
    Callable<Integer> worker=jobs.get(i);
    Future<Integer> submit=executor.submit(worker);
    list.add(submit);
  }
  executor.shutdown();
  while(!executor.isTerminated()) {}
  do something with the returned data;
}
}

Вызываемый класс

public class WorkCallable implements Callable<Integer>{
 @Override
 public Integer call() throws Exception{
   Properties props=new Properties();
   props.put("annotators", "tokenize, ssplit, pos");
   StanfordCoreNLP pipeline=new StanfordCoreNLP(props);
   for(String id:entry.keySet()){
   Annotation document=new Annotation(entry.get(id));
   pipeline.annotate(document);

   process the data;
   return an integer value;
 }
}

Проблема в том, что, когда я проверяю, сколько потоков запущено, я обнаружил очень мало, и кажется, что исполнитель не использует преимущества идеальных ядер!

надеюсь описание понятно.

Обновление:

  • Используемая библиотека представляет собой пакет StanfordCoreNLP для обработки текста, передаваемого объекту Callable, в виде карты идентификатора документа и его содержимого. Обработка данных не является проблемой, так как у меня все отлично работает без включения библиотеки StanfordCoreNLP. Другими словами, неглубокая обработка документов работает нормально и использует все ядра. Но когда я включаю этот пакет, это не так.

person DotNet    schedule 18.08.2012    source источник
comment
Ваше занятое ожидание с while(!executor.isTerminated()) {} тут же занимает одно ядро. Вы не должны этого делать. Для ваших целей есть awaitTermination. Кстати, какое именно количество очень мало и сколько ядер вы знаете за тот факт, что у вас есть? А еще лучше, что печатает System.out.println(Runtime.getRuntime().availableProcessors())?   -  person Marko Topolnik    schedule 18.08.2012
comment
Описание тоже не очень понятное. Загрузка некоторых библиотек и обработка данных звучат как потенциально синхронизированные операции, которые могут вывести из состояния выполнения все потоки, кроме одного.   -  person mazaneicha    schedule 18.08.2012
comment
Спасибо за ваши комментарии Марко и mazaneicha. Я согласен, что загрузка библиотек может быть проблемой.   -  person DotNet    schedule 19.08.2012


Ответы (2)


Если вы используете Windows, то JVM делегирует планирование потока ядру NT. Операционные системы типа POSIX сопоставляют потоки ОС напрямую с JVM и планируют совместно.

Однако, что бы ни случилось, вы не сможете гарантировать, что потоки распределяются равномерно по ядрам/процессорам. Что-то еще в ОС может работать на ядре 4, когда вы запускаете свой 4-й поток, поэтому он может быть запланирован для другого ядра. Или планировщик может решить разместить их на одном ядре.

person Dan    schedule 18.08.2012
comment
Спасибо, Дэн. Ну, даже если что-то работает на ядре 4, это должно отображаться как работающий процесс, которого нет, и идентификатор ядра идеален в отношении top и htop. - person DotNet; 19.08.2012

На данный момент с предоставленной вами информацией я подозреваю, что между потоками существует некоторый конфликт, поэтому вероятность такова, что некоторые потоки заблокированы/ожидают. Чтобы убедиться в этом, вы можете использовать JVisual VM и сделать дамп потока (Jconsole также является опцией). JVisual VM — это утилита для мониторинга java-приложений, которая поставляется с JDK. Если вы не использовали это раньше, это было бы хорошей инвестицией вашего времени, чтобы узнать о нем, поскольку он очень полезен и прост в использовании.

см. здесь JVisualVM

  1. Подключитесь к вашей программе с помощью JVisual VM Take Thread dump.
  2. Это предоставит вам состояние потоков в вашей программе в этот момент времени, если есть конфликт и / или блокировка, это будет легко обнаружить с помощью дампа потока.
  3. Не стесняйтесь вставлять его сюда, если вы не можете понять, что происходит в дампе потока, хотя есть ряд ресурсов, которые помогут вам понять дамп потока в Интернете.

С другой стороны, как указал @Marko, вы могли бы более эффективно обрабатывать завершение работы исполнителей, и я бы сказал, что ExecutorCompletionService будет соответствовать вашим требованиям и сделает код более элегантным и удобным для чтения. Отметьте здесь ExecutorCompletionService. бездействующих ядер может быть, вы можете провести рефакторинг для использования ECS.

person soody    schedule 20.08.2012
comment
Большое спасибо soody за подробный комментарий. Я проверю ресурсы, которые вы разместили, и вернусь с более подробной информацией. - person DotNet; 20.08.2012