Вопросы для начинающих по многопоточности в Java - PullRequest
9 голосов
/ 07 октября 2009

Я работаю над научным приложением, в котором есть легко разделяемые части, которые могут работать параллельно. Итак, я написал эти части для каждого запуска как независимые потоки, хотя и не для того, что кажется стандартной причиной разделения вещей на потоки (то есть, не блокируя какую-либо команду выхода или тому подобное).

Несколько вопросов:

Купит ли это мне что-нибудь на стандартных многоядерных десктопах - т. Е. Будут ли потоки работать на отдельных ядрах, если у меня установлена ​​текущая JVM, или мне придется делать что-то еще?

У меня есть несколько объектов, которые читаются (хотя и не пишутся) всеми потоками. Потенциальные проблемы с этим? Решения этих проблем?

Для реальных кластеров, можете ли вы порекомендовать каркасам для распределения потоков по различным узлам, чтобы мне не приходилось управлять этим самостоятельно (ну, если таковые существуют)? ПОЯСНЕНИЕ: под этим я подразумеваю либо то, что автоматически преобразует потоки в задачу для отдельных узлов, либо делает весь кластер похожим на одну JVM (то есть, таким образом, он может отправлять потоки любым процессорам, к которым он может получить доступ), или чему угодно. По сути, реализуйте распараллеливание полезным способом в кластере, учитывая, что я встроил его в алгоритм, с минимальными затратами труда с моей стороны.

Бонус: Большая часть оценки состоит из сравнений наборов (например, объединение, пересечение, содержит) с некоторым отображением ключей, чтобы получить соответствующие наборы. У меня есть некоторый ограниченный опыт работы с FORTRAN, C и C ++ (семестр научных вычислений для первого и HS AP классы 10 лет назад для двух других) - какую скорость / простоту выигрыша в распараллеливании я мог бы найти, если бы связал свой Java-интерфейс к алгоритмическому бэк-энду на одном из этих языков, и какую головную боль может испытать мой уровень опыта при реализации этих операций на этих языках?

Ответы [ 3 ]

8 голосов
/ 07 октября 2009
  • Да, использование независимых потоков приведет к использованию нескольких ядер в обычной JVM без необходимости выполнять какую-либо работу.

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

  • Я не уверен, какую кластеризацию вы рассматриваете, но вы можете взглянуть на Hadoop . Обратите внимание, что распределенные вычисления распределяют задач , а не потоков (обычно в любом случае).

5 голосов
/ 07 октября 2009

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

Среды выполнения Java обычно планируют одновременную работу потоков на всех доступных процессорах и ядрах. Я думаю, что это можно ограничить, но это потребует дополнительной работы; по умолчанию ограничений нет.

Неизменяемые объекты

Для объектов, доступных только для чтения, объявите их поля-члены как final, что обеспечит их назначение при создании объекта и его изменение никогда не будет. Если поле не final, даже если оно никогда не изменялось после создания, в многопоточной программе могут возникать проблемы с «видимостью». Это может привести к тому, что назначения, сделанные одним потоком, никогда не станут видимыми для другого.

Любые изменяемые поля, доступ к которым осуществляется несколькими потоками, должны быть объявлены volatile, защищены синхронизацией или использовать какой-либо другой механизм параллелизма, чтобы обеспечить согласованность и видимость изменений среди потоков.

Распределенные вычисления

Наиболее широко используемый фреймворк для распределенной обработки такого типа в Java называется Hadoop. В нем используется парадигма, называемая map-Reduce.

Интеграция с собственным кодом

Интеграция с другими языками вряд ли имеет смысл. Благодаря своему адаптивному компилятору байт-кода к нативному Java уже очень быстро справляется с широким спектром вычислительных задач. Было бы неправильно предполагать, что другой язык быстрее без реального тестирования. Кроме того, интеграция с «нативным» кодом с использованием JNI чрезвычайно утомительна, подвержена ошибкам и сложна; Использование более простых интерфейсов, таких как JNA, очень медленное и быстро стирает любой выигрыш в производительности.

1 голос
/ 07 октября 2009

Как говорили некоторые люди, ответы таковы:

  1. Резьбы на сердечниках - да. Java уже давно поддерживает нативные потоки. В большинстве ОС предусмотрены потоки ядра, которые автоматически планируются для любых имеющихся у вас процессоров (производительность реализации зависит от ОС).

  2. Простой ответ: в целом это будет безопасно. Более сложный ответ заключается в том, что вы должны убедиться, что ваш Объект действительно создан и инициализирован, прежде чем какой-либо поток сможет получить к нему доступ. Это решается одним из двух способов:

    • Пусть загрузчик классов решит проблему за вас с помощью Singleton (и отложенной загрузки классов):

      public class MyImmutableObject
      {
          private static class MyImmutableObjectInstance {
              private static final MyImmutableObject instance = new MyImmutableObject();
          }
          public MyImmutableObject getInstance() {
              return MyImmutableObjectInstance.instance;
          }
      }
      
    • Явное использование семантики получения / выпуска для обеспечения согласованной модели памяти:

      MyImmutableObject foo = null;
      volatile bool objectReady = false;
      
      // initializer thread:
      ....
      /// create & initialize object for use by multiple threads
      foo = new MyImmutableObject();
      foo.initialize();
      
      // release barrier
      objectReady = true;
      
      // start worker threads
      public void run() {
         // acquire barrier
         if (!objectReady)
             throw new IllegalStateException("Memory model violation");
      
         // start using immutable object foo
      }
      

    Я не припоминаю, как вы можете использовать модель памяти Java для выполнения последнего случая. Я считаю, если я правильно помню, что запись в энергозависимую переменную эквивалентна барьеру освобождения, в то время как чтение из энергозависимой переменной эквивалентно барьеру получения. Кроме того, причина создания логической переменной volatile в отличие от объекта заключается в том, что доступ к volatile переменной является более дорогостоящим из-за ограничений модели памяти - таким образом, boolean позволяет применять модель памяти, и тогда доступ к объекту может быть выполнен намного быстрее в потоке.

  3. Как уже упоминалось, есть все виды механизмов RPC. Есть также RMI, который является нативным подходом для запуска кода на удаленных целях. Есть также фреймворки, такие как Hadoop, которые предлагают более полное решение, которое может быть более подходящим.

  4. Для вызова нативного кода это довольно уродливо - Sun действительно не рекомендует использовать его, превращая JNI в ужасно сложный беспорядок, но это возможно. Я знаю, что была по крайней мере одна коммерческая среда Java для загрузки и выполнения собственных динамических библиотек без необходимости беспокоиться о JNI (не уверен, есть ли какие-либо бесплатные проекты или проекты OSS).

Удачи.

...