2012-06-29 2 views
16

Я прочитал о реализации структуры Fork/Join, которая была представлена ​​на Java 7, и я просто хотел проверить, что я понимаю, как работает магия.java Fork/Объяснить использование стека

Как я понимаю, когда поток вилки создает в своей очереди подзадачи (какой другой поток может или не может украсть). Когда поток пытается «присоединиться», он фактически проверяет свою очередь на существующие задачи, а затем рекурсивно выполняет их, что означает, что для любой операции «join» - 2 кадра будут добавлены в стек вызовов потока (один для соединения и один для нового принятого вызова задачи).

Как я знаю, JVM не поддерживает оптимизацию хвостовых вызовов (которая может служить в этой ситуации для удаления кадра стека метода соединения) Я считаю, что при выполнении сложной операции с большим количеством вилок и объединений поток может бросать a StackOverflowError.

Я прав, или они нашли какой-нибудь классный способ предотвратить это?

EDIT

Вот сценарий, чтобы помочь уточняющий вопрос: Say (для простоты), что у нас есть только один поток в forkjoin бассейне. В некоторый момент времени - вилки потоков, а затем звонки соединяются. В то время как в методе соединения поток обнаруживает, что он может выполнить разветвленную задачу (как она найдена в ее очереди), поэтому она вызывает следующую задачу. Эта задача, в свою очередь, вилки, а затем вызывает join - поэтому при выполнении метода соединения поток найдет разветвленную задачу в своей очереди (как и раньше) и вызовет ее. на этом этапе стек вызовов будет содержать как минимум фреймы для двух объединений и две задачи.

, поскольку вы можете видеть, что каркас преобразования fork преобразован в обычную рекурсию. Поскольку java не поддерживает оптимизацию хвостового вызова - каждая рекурсия в java может вызвать StackOverflowError, если она будет достаточно глубокой.

Мой вопрос - сделал реализатор инфраструктуры fork/join найти какой-нибудь классный способ предотвратить эту ситуацию.

+0

@assylias - уверенный, если вы можете сэкономить очки :) – bennyl

ответ

6

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

Возможно, вы понимаете, почему учебник по JavaDoc разбивает каждую подзадачу пополам.

+0

слишком плохо .. я надеялся на остроумный алгоритм .. – bennyl

2

Обычно каждая новая задача, наложенная на стек, вдвое меньше предыдущей. Таким образом, объем работы растет экспоненциально с размером стека. Даже с крошечным стеком, вы сможете поместиться на более чем достаточно работы, чтобы вы были заняты в течение некоторого времени.

+0

Я думаю, что вы меня неправильно поняли, я имел в виду стек памяти потока/стек вызовов. – bennyl

1

Надеюсь, я правильно вас пойму.

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

Очень интересное место для метода fork - это ForkJoinWorkerThread.pushTask с использованием небезопасного объекта, поэтому вам следует обратить внимание, что массив используется для хранения задач.

EDIT: Первое и простое - когда вы находитесь в верхней части очереди, вы просто распаковываете и выполняете, и возвращается retult. (forkjointask.java:353)

Разный подход используется, когда у вас есть зависимости, в этом случае элемент управления возвращается в WorkerThread, который затем отвечает за обнаружение цепей и их выполнение. Первый работник проверяет местную очередь на любые незавершенные задачи, и если таких задач он не выполняет, то передается задание и возвращает результат, иначе он переходит к следующему случаю. Это помогает крадучикам несколько раз. Ничто не могло помочь ...повторы, которые на первом шаге равны MAX_HELP, теперь равны нулю - управление передается в пул, который выполняет несколько проверок и выполняет tryAwaitDone. И в этом методе wait вызывается для ожидания завершения задачи.

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

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

Смежные вопросы