В Java есть структура параллельных задач, похожая на Thread Building Blocks - она называется средой Fork-Join. доступно для использования с текущей Java SE 6 и будет включено в будущую Java SE 7.
В дополнение к документации по классу javadoc доступны ресурсы для начала работы с платформой. На странице jsr166 упоминается, что
«Существует также вики, содержащая дополнительную документацию, заметки, советы, примеры и т. Д. Для этих классов».
Хорошие примеры для начала - примеры , такие как умножение матриц.
Я использовал инфраструктуру fork-join для решения некоторых проблем Intel с потоками 2009 года . Фреймворк является легковесным и с минимальными издержками - моя была единственной записью Java для задачи Kight's Tour, и она превзошла другие записи в конкурсе. Исходные тексты java и рецензии доступны для загрузки на сайте испытаний.
РЕДАКТИРОВАТЬ:
Понятия не имею, как класс или кусочки
кода может выглядеть после нажатия на
бассейн [...]
Вы можете создать свою собственную задачу, создав подклассы одного из подклассов ForKJoinTask , таких как RecursiveTask . Вот как можно вычислить последовательность Фибоначчи параллельно. (Взято из RecursiveTask
javadocs - комментарии мои.)
// declare a new task, that itself spawns subtasks.
// The task returns an Integer result.
class Fibonacci extends RecursiveTask<Integer> {
final int n; // the n'th number in the fibonacci sequence to compute
Fibonnaci(int n) { this.n = n; } // constructor
Integer compute() { // this method is the main work of the task
if (n <= 1) // 1 or 0, base case to end recursion
return n;
Fibonacci f1 = new Fibonacci(n - 1); // create a new task to compute n-1
f1.fork(); // schedule to run asynchronously
Fibonacci f2 = new Fibonacci(n - 2); // create a new task to compute n-2
return f2.invoke() + f1.join(); // wait for both tasks to compute.
// f2 is run as part of this task, f1 runs asynchronously.
// (you could create two separate tasks and wait for them both, but running
// f2 as part of this task is a little more efficient.
}
}
Затем вы запустите эту задачу и получите результат
// default parallelism is number of cores
ForkJoinPool pool = new ForkJoinPool();
Fibonacci f = new Fibonacci(100);
int result = pool.invoke(f);
Это тривиальный пример простоты. На практике производительность не будет такой хорошей, поскольку работа, выполняемая задачей, является тривиальной по сравнению с издержками инфраструктуры задач. Как правило, задача должна выполнять некоторые важные вычисления - достаточно, чтобы сделать служебные данные инфраструктуры незначительными, но не настолько, чтобы в конце задачи было одно ядро, выполняющее одну большую задачу. Разделение больших задач на более мелкие гарантирует, что одно ядро не будет выполнять большую работу, в то время как другие ядра бездействуют - использование меньших задач позволяет загружать больше ядер, но не настолько, чтобы задача не выполнялась.
[...] или как может выглядеть странный код, когда
вам нужно сделать копию всего
и сколько всего толкается
в бассейн.
Только сами задачи помещаются в пул. В идеале вы не хотите копировать что-либо: чтобы избежать вмешательства и необходимости блокирования, которое могло бы замедлить вашу программу, ваши задачи в идеале должны работать с независимыми данными. Данные только для чтения могут совместно использоваться всеми задачами, и их не нужно копировать. Если потоки должны сотрудничать при создании какой-то большой структуры данных, лучше всего, чтобы они строили части по отдельности, а затем объединяли их в конце. Объединение может быть выполнено как отдельная задача, или каждая задача может добавить свою часть головоломки к общему решению. Это часто требует некоторой формы блокировки, но это не является существенной проблемой производительности, если работа задачи намного больше, чем работа, обновляющая решение. Решение My Knight's Tour использует этот подход для обновления общего репозитория туров на доске.
Работа с задачами и параллелизмом является довольно серьезным изменением парадигмы от обычного однопоточного программирования. Часто можно решить несколько задач, но только некоторые из них подойдут для резьбового решения. Может потребоваться несколько попыток, чтобы понять, как преобразовывать знакомые проблемы многопоточным способом. Лучший способ научиться - это посмотреть на примеры, а затем попробовать сами. Всегда профилируйте и измеряйте эффекты от изменения количества потоков. Вы можете явно указать количество потоков (ядер) для использования в пуле в конструкторе пула. Когда задачи разбиваются линейно, вы можете ожидать почти линейного ускорения по мере увеличения числа потоков.