Spec-Zone .ru
спецификации, руководства, описания, API
|
public abstract class CountedCompleter<T> extends ForkJoinTask<T>
ForkJoinTask
с действием завершения, выполняемым когда инициировано и нет никаких остающихся действий на ожидании. CountedCompleters вообще более устойчивы в присутствии остановов подзадачи и блокирования, чем другие формы ForkJoinTasks, но менее интуитивны к программе. Использование CountedCompleter подобно таковым из другого завершения базируемые компоненты (такой как CompletionHandler
) за исключением того, что многократные завершения на ожидании могут быть необходимыми, чтобы инициировать действие завершения onCompletion(java.util.concurrent.CountedCompleter<?>)
, не только один. Если не инициализировано иначе, количество на ожидании запускается в нуле, но может быть (атомарно) изменено, используя методы setPendingCount(int)
, addToPendingCount(int)
, и compareAndSetPendingCount(int, int)
. На вызов tryComplete()
, если количество действия на ожидании является ненулевым, оно постепенно уменьшается; иначе, действие завершения выполняется, и если у этого самого completer есть completer, процесс продолжается с его completer. Как имеет место со связанными компонентами синхронизации такой как Phaser
и Semaphore
, эти методы влияют только на внутренние количества; они не устанавливают дальнейшую внутреннюю бухгалтерию. В частности идентификационные данные задач на ожидании не сохраняются. Как иллюстрировано ниже, можно создать подклассы, которые действительно записывают некоторых или все задачи на ожидании или их результаты при необходимости. Как иллюстрировано ниже, служебные методы, поддерживающие настройку обходов завершения, также обеспечиваются. Однако, потому что CountedCompleters обеспечивают только основные механизмы синхронизации, может быть полезно создать дальнейшие абстрактные подклассы, которые поддерживают редактирования, поля, и дополнительные методы поддержки, подходящие для ряда связанных использований. Конкретный CountedCompleter class должен определить метод compute()
, это должно в большинстве случаев (как иллюстрировано ниже), вызывать tryComplete()
однажды возврат. class может также дополнительно переопределить метод onCompletion(java.util.concurrent.CountedCompleter<?>)
выполнять действие после нормального завершения, и метод onExceptionalCompletion(java.lang.Throwable, java.util.concurrent.CountedCompleter<?>)
выполнять действие на любое исключение.
CountedCompleters чаще всего не переносят результаты, когда они обычно объявляются как CountedCompleter<Void>
, и будет всегда возвращаться null
в результате значение. В других случаях следует переопределить метод getRawResult()
обеспечить следствие join(), invoke()
, и связанные методы. Вообще, этот метод должен возвратить значение поля (или функция одного или более полей) объекта CountedCompleter, который содержит результат после завершения. Метод setRawResult(T)
значением по умолчанию не играет роли в CountedCompleters. Это возможно, но редко применимо, чтобы переопределить этот метод, чтобы поддержать другие объекты или поля, содержащие данные результата.
CountedCompleter, у которого самостоятельно нет completer (то есть, один, для который getCompleter()
возвраты null
) может использоваться в качестве регулярного ForkJoinTask с этой добавленной функциональностью. Однако, любой completer, у которого поочередно есть другой completer подачи только как внутренний помощник для других вычислений, таким образом, его собственное состояние задачи (столь же сообщил в методах о таком как ForkJoinTask.isDone()
) произвольно; это состояние изменяется только на явные вызовы complete(T)
, ForkJoinTask.cancel(boolean)
, ForkJoinTask.completeExceptionally(java.lang.Throwable)
или после исключительного завершения метода compute
. После любого исключительного завершения исключение может быть передано к completer задачи (и его completer, и так далее), если Вы существуете, и это иначе уже не завершилось. Точно так же отмена внутреннего CountedCompleter имеет только локальный эффект на это completer, так не часто полезно.
Демонстрационные Использования.
Найдите что-либо подобное рекурсивному разложению. CountedCompleters может быть расположен в деревьях, подобных часто используемым с RecursiveAction
s, хотя конструкции, включенные в установку их обычно, изменяются. Здесь, completer каждой задачи является своим родителем в дереве вычисления. Даже при том, что они влекут за собой немного больше бухгалтерии, CountedCompleters может быть лучшими вариантами, применяя возможно отнимающую много времени работу (который не может быть далее подразделен) к каждому элементу массива или набора; особенно, когда работа занимает существенно отличающееся количество времени, чтобы завершиться для некоторых элементов чем другие, любой из-за внутреннего изменения (например ввод-вывод) или вспомогательные эффекты, такие как сборка "мусора". Поскольку CountedCompleters обеспечивают свои собственные продолжения, другие потоки не должны блокировать ожидание, чтобы выполнить их.
Например, вот начальная версия class, который использует, "делятся на два" рекурсивных разложения, чтобы разделить работу на единственные части (листовые задачи). Даже когда работа разделяется на отдельные вызовы, основанные на дереве методы обычно предпочтительны для непосредственно разветвляющихся листовых задач, потому что они уменьшают передачу межпотока и улучшают выравнивание нагрузки. В рекурсивном случае, второй из каждой пары подзадач, чтобы закончить триггерное завершение ее родителя (потому что никакая комбинация результата не выполняется, значение по умолчанию никакая-op реализация метода onCompletion
не переопределяется). Статический служебный метод устанавливает основную задачу и вызывает ее (здесь, неявно используя ForkJoinPool.commonPool()
).
class MyOperation<E> { void apply(E e) { ... } }
class ForEach<E> extends CountedCompleter<Void> {
public static <E> void forEach(E[] array, MyOperation<E> op) {
new ForEach<E>(null, array, op, 0, array.length).invoke();
}
final E[] array; final MyOperation<E> op; final int lo, hi;
ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
super(p);
this.array = array; this.op = op; this.lo = lo; this.hi = hi;
}
public void compute() { // version 1
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
setPendingCount(2); // must set pending count before fork
new ForEach(this, array, op, mid, hi).fork(); // right child
new ForEach(this, array, op, lo, mid).fork(); // left child
}
else if (hi > lo)
op.apply(array[lo]);
tryComplete();
}
}
Этот проект может быть улучшен, замечая, что в рекурсивном случае, задаче нечего делать после разветвления ее правильной задачи, так может непосредственно вызвать ее левую задачу перед возвратом. (Это - аналог удаления хвостовой рекурсии.) Кроме того, потому что задача возвращается после выполнения ее левой задачи (вместо того, чтобы проваливаться, чтобы вызвать tryComplete
) количество на ожидании устанавливается в одного:
class ForEach<E> ...
public void compute() { // version 2
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
setPendingCount(1); // only one pending
new ForEach(this, array, op, mid, hi).fork(); // right child
new ForEach(this, array, op, lo, mid).compute(); // direct invoke
}
else {
if (hi > lo)
op.apply(array[lo]);
tryComplete();
}
}
Как дальнейшее улучшение, заметьте, что левая задача не должна даже существовать. Вместо того, чтобы создать новый, мы можем выполнить итерации использования исходной задачи, и добавить счет на ожидании для каждого ветвления. Дополнительно, потому что никакая задача в этом дереве не реализует onCompletion(java.util.concurrent.CountedCompleter<?>)
метод, tryComplete()
может быть заменен propagateCompletion()
.
class ForEach<E> ...
public void compute() { // version 3
int l = lo, h = hi;
while (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
new ForEach(this, array, op, mid, h).fork(); // right child
h = mid;
}
if (h > l)
op.apply(array[l]);
propagateCompletion();
}
Дополнительные улучшения таких классов могли бы повлечь за собой предварительные вычисления, ожидающие количества так, чтобы они могли быть установлены в конструкторах, специализируя классы для листовых шагов, подразделяя говорит, четыре, вместо два на итерацию, и использование адаптивного порога вместо того, чтобы всегда подразделить вниз к единственным элементам. Поиск. Дерево CountedCompleters может искать значение или свойство в различных частях структуры данных, и сообщить о результате в AtomicReference
как только каждый находится. Другие могут опросить результат избежать ненужной работы. (Вы могли дополнительно отменить другие задачи, но это обычно более просто и более эффективно только позволить им замечать, что результат устанавливается, и раз так пропустите дальнейшую обработку.) Иллюстрирующий снова с массивом, использующим полное разделение (снова, практически, листовые задачи будут почти всегда обрабатывать больше чем один элемент):
class Searcher<E> extends CountedCompleter<E> {
final E[] array; final AtomicReference<E> result; final int lo, hi;
Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
super(p);
this.array = array; this.result = result; this.lo = lo; this.hi = hi;
}
public E getRawResult() { return result.get(); }
public void compute() { // similar to ForEach version 3
int l = lo, h = hi;
while (result.get() == null && h >= l) {
if (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
new Searcher(this, array, result, mid, h).fork();
h = mid;
}
else {
E x = array[l];
if (matches(x) && result.compareAndSet(null, x))
quietlyCompleteRoot(); // root task is now joinable
break;
}
}
tryComplete(); // normally complete whether or not found
}
boolean matches(E e) { ... } // return true if found
public static <E> E search(E[] array) {
return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
}
}
В этом примере, так же как других, в которых задачи не имеют никаких других эффектов кроме к compareAndSet общий результат, запаздывающий безусловный вызов tryComplete
мог быть сделан условным выражением (if (result.get() == null) tryComplete();
) потому что никакая дальнейшая бухгалтерия не обязана управлять завершениями, как только корневая задача завершается. Запись подзадач. Задачи CountedCompleter, которые комбинируют результаты многократных подзадач обычно, должны получать доступ к этим результатам в методе onCompletion(java.util.concurrent.CountedCompleter<?>)
. Как иллюстрировано в следующем class (который выполняет упрощенную форму карты - уменьшают, где отображения и сокращения являются всем типом E
), один способ прикончить у этого дележа и завоевать проекты должна быть каждая запись подзадачи ее одноуровневый элемент, так, чтобы к этому можно было получить доступ в методе onCompletion
. Этот метод применяется к сокращениям, в которых не имеет значения порядок объединения левых и правых результатов; упорядоченные сокращения требуют явных левых/правильных обозначений. Разновидности другого streamlinings, замеченного в вышеупомянутых примерах, могут также применяться.
class MyMapper<E> { E apply(E v) { ... } }
class MyReducer<E> { E apply(E x, E y) { ... } }
class MapReducer<E> extends CountedCompleter<E> {
final E[] array; final MyMapper<E> mapper;
final MyReducer<E> reducer; final int lo, hi;
MapReducer<E> sibling;
E result;
MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
MyReducer<E> reducer, int lo, int hi) {
super(p);
this.array = array; this.mapper = mapper;
this.reducer = reducer; this.lo = lo; this.hi = hi;
}
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
left.sibling = right;
right.sibling = left;
setPendingCount(1); // only right is pending
right.fork();
left.compute(); // directly execute left
}
else {
if (hi > lo)
result = mapper.apply(array[lo]);
tryComplete();
}
}
public void onCompletion(CountedCompleter<?> caller) {
if (caller != this) {
MapReducer<E> child = (MapReducer<E>)caller;
MapReducer<E> sib = child.sibling;
if (sib == null || sib.result == null)
result = child.result;
else
result = reducer.apply(child.result, sib.result);
}
}
public E getRawResult() { return result; }
public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
return new MapReducer<E>(null, array, mapper, reducer,
0, array.length).invoke();
}
}
Здесь, метод onCompletion
принимает форму, характерную для многих проектов завершения, которые комбинируют результаты. Этот метод стиля обратного вызова инициирован однажды на задачу, или в двух различных контекстов, в которых количество на ожидании, или становится, нуль: (1) задачей непосредственно, если ее количество на ожидании является нулем на вызов tryComplete
, или (2) любой из его подзадач, когда они завершают и постепенно уменьшают количество на ожидании, чтобы обнулить. caller
параметр отличает случаи. Чаще всего, когда вызывающая сторона this
, никакое действие не необходимо. Иначе параметр вызывающей стороны может использоваться (обычно через бросок), чтобы предоставить значение (и/или соединяется с другими значениями) быть объединенным. Принимая правильное использование количеств на ожидании, действия внутри onCompletion
происходите (однажды) после завершения задачи и ее подзадач. Никакая дополнительная синхронизация не требуется в пределах этого метода гарантировать потокобезопасность доступов к полям этой задачи или других завершенных задач. Обходы завершения. Используя onCompletion
обработать завершения является неподходящим или неудобным, можно использовать методы firstComplete()
и nextComplete()
создать пользовательские обходы. Например, чтобы определить MapReducer, который только разделяет правые задачи в форме третьего примера ForEach, завершения должны совместно уменьшить вдоль неисчерпанных ссылок подзадачи, которые могут быть сделаны следующим образом:
class MapReducer<E> extends CountedCompleter<E> { // version 2
final E[] array; final MyMapper<E> mapper;
final MyReducer<E> reducer; final int lo, hi;
MapReducer<E> forks, next; // record subtask forks in list
E result;
MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
super(p);
this.array = array; this.mapper = mapper;
this.reducer = reducer; this.lo = lo; this.hi = hi;
this.next = next;
}
public void compute() {
int l = lo, h = hi;
while (h - l >= 2) {
int mid = (l + h) >>> 1;
addToPendingCount(1);
(forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork;
h = mid;
}
if (h > l)
result = mapper.apply(array[l]);
// process completions by reducing along and advancing subtask links
for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
t.result = reducer.apply(t.result, s.result);
}
}
public E getRawResult() { return result; }
public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
return new MapReducer<E>(null, array, mapper, reducer,
0, array.length, null).invoke();
}
}
Триггеры. Некоторые CountedCompleters самостоятельно никогда не разветвляются, но вместо этого служат битами инфраструктуры в других проектах; включая тех, в которых завершение одной из большего количества асинхронных задач инициировало другую асинхронную задачу. Например:
class HeaderBuilder extends CountedCompleter<...> { ... }
class BodyBuilder extends CountedCompleter<...> { ... }
class PacketSender extends CountedCompleter<...> {
PacketSender(...) { super(null, 1); ... } // trigger on second completion
public void compute() { } // never called
public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
}
// sample use:
PacketSender p = new PacketSender();
new HeaderBuilder(p, ...).fork();
new BodyBuilder(p, ...).fork();
Модификатор | Конструктор и Описание |
---|---|
protected |
CountedCompleter()
Создает новый CountedCompleter без completer и начального количества на ожидании нуля.
|
protected |
CountedCompleter(CountedCompleter<?> completer)
Создает новый CountedCompleter с данным completer и начальным количеством на ожидании нуля.
|
protected |
CountedCompleter(CountedCompleter<?> completer, int initialPendingCount)
Создает новый CountedCompleter с данным completer и начальным количеством на ожидании.
|
Модификатор и Тип | Метод и Описание |
---|---|
void |
addToPendingCount(int delta)
Добавляет (атомарно) данное значение к количеству на ожидании.
|
boolean |
compareAndSetPendingCount(int expected, int count)
Наборы (атомарно) количество на ожидании к данному количеству, только если это в настоящий момент содержит данное математическое ожидание.
|
void |
complete(T rawResult)
Независимо от количества на ожидании, вызывает
onCompletion(java.util.concurrent.CountedCompleter<?>) , метки эта задача как полные и дальнейшие триггеры tryComplete() на completer этой задачи, если Вы существуете. |
abstract void |
compute()
Основное вычисление выполняется этой задачей.
|
int |
decrementPendingCountUnlessZero()
Если количество на ожидании является ненулевым, (атомарно) декременты это.
|
protected boolean |
exec()
Соглашения выполнения реализаций для CountedCompleters.
|
CountedCompleter<?> |
firstComplete()
Если количество этой задачи на ожидании является нулем, возвращает эту задачу; иначе постепенно уменьшает его количество на ожидании и возвраты
null . |
CountedCompleter<?> |
getCompleter()
Возвращает completer, установленный в конструкторе этой задачи, или
null если ни один. |
int |
getPendingCount()
Возвращает текущее количество на ожидании.
|
T |
getRawResult()
Возвращает результат вычисления.
|
CountedCompleter<?> |
getRoot()
Возвращает корень текущего вычисления; то есть, эта задача, если у этого нет никакого completer, еще корень его completer.
|
CountedCompleter<?> |
nextComplete()
Если у этой задачи нет completer, вызывает
ForkJoinTask.quietlyComplete() и возвраты null . |
void |
onCompletion(CountedCompleter<?> caller)
Выполняет действие когда метод
tryComplete() вызывается и количество на ожидании является нулем, или когда безусловный метод complete(T) вызывается. |
boolean |
onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller)
Выполняет действие когда метод
ForkJoinTask.completeExceptionally(java.lang.Throwable) вызывается или метод compute() выдает исключение, и эта задача иначе уже обычно не завершалась. |
void |
propagateCompletion()
Эквивалентный
tryComplete() но не вызывает onCompletion(java.util.concurrent.CountedCompleter<?>) вдоль пути завершения: Если количество на ожидании является ненулевым, постепенно уменьшает количество; иначе, так же попытки завершить completer этой задачи, если Вы существуете, еще отмечают эту задачу как полную. |
void |
quietlyCompleteRoot()
Эквивалентный
getRoot().quietlyComplete() . |
void |
setPendingCount(int count)
Устанавливает количество на ожидании в данное значение.
|
protected void |
setRawResult(T t)
Метод, который переносящий результат CountedCompleters может дополнительно использовать, чтобы помочь поддержать данные результата.
|
void |
tryComplete()
Если количество на ожидании является ненулевым, постепенно уменьшает количество; иначе вызывает
onCompletion(java.util.concurrent.CountedCompleter<?>) и затем так же попытки завершить completer этой задачи, если Вы существуете, еще отмечают эту задачу как полную. |
adapt, adapt, adapt, cancel, compareAndSetForkJoinTaskTag, completeExceptionally, fork, get, get, getException, getForkJoinTaskTag, getPool, getQueuedTaskCount, getSurplusQueuedTaskCount, helpQuiesce, inForkJoinPool, invoke, invokeAll, invokeAll, invokeAll, isCancelled, isCompletedAbnormally, isCompletedNormally, isDone, join, peekNextLocalTask, pollNextLocalTask, pollTask, quietlyComplete, quietlyInvoke, quietlyJoin, reinitialize, setForkJoinTaskTag, tryUnfork
protected CountedCompleter(CountedCompleter<?> completer, int initialPendingCount)
completer
- completer этой задачи, или null
если ни одинinitialPendingCount
- начальное количество на ожиданииprotected CountedCompleter(CountedCompleter<?> completer)
completer
- completer этой задачи, или null
если ни одинprotected CountedCompleter()
public abstract void compute()
public void onCompletion(CountedCompleter<?> caller)
tryComplete()
вызывается и количество на ожидании является нулем, или когда безусловный метод complete(T)
вызывается. По умолчанию этот метод ничего не делает. Можно отличить случаи, проверяя идентификационные данные данного параметра вызывающей стороны. Если не равный this
, тогда это обычно - подзадача, которая может содержать результаты (и/или соединяется с другими результатами) объединиться.caller
- задача, вызывающая этот метод (который может быть этой задачей непосредственно).public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller)
ForkJoinTask.completeExceptionally(java.lang.Throwable)
вызывается или метод compute()
выдает исключение, и эта задача иначе уже обычно не завершалась. На записи в этот метод, эту задачу ForkJoinTask.isCompletedAbnormally()
. Возвращаемое значение этого метода управляет дальнейшим распространением: Если true
и у этой задачи есть completer, тогда этот completer также завершается исключительно. Реализация по умолчанию этого метода не делает ничего кроме возврата true
.ex
- исключениеcaller
- задача, вызывающая этот метод (который может быть этой задачей непосредственно).public final CountedCompleter<?> getCompleter()
null
если ни один.public final int getPendingCount()
public final void setPendingCount(int count)
count
- количествоpublic final void addToPendingCount(int delta)
delta
- значение, чтобы добавитьpublic final boolean compareAndSetPendingCount(int expected, int count)
expected
- математическое ожиданиеcount
- новое значениеpublic final int decrementPendingCountUnlessZero()
public final CountedCompleter<?> getRoot()
public final void tryComplete()
onCompletion(java.util.concurrent.CountedCompleter<?>)
и затем так же попытки завершить completer этой задачи, если Вы существуете, еще отмечают эту задачу как полную.public final void propagateCompletion()
tryComplete()
но не вызывает onCompletion(java.util.concurrent.CountedCompleter<?>)
вдоль пути завершения: Если количество на ожидании является ненулевым, постепенно уменьшает количество; иначе, так же попытки завершить completer этой задачи, если Вы существуете, еще отмечают эту задачу как полную. Этот метод может быть полезным в случаях где onCompletion
не должен, или нуждаться не, вызываться для каждого completer в вычислении.public void complete(T rawResult)
onCompletion(java.util.concurrent.CountedCompleter<?>)
, метки эта задача как полные и дальнейшие триггеры tryComplete()
на completer этой задачи, если Вы существуете. Данный rawResult используется в качестве параметра setRawResult(T)
перед вызовом onCompletion(java.util.concurrent.CountedCompleter<?>)
или маркировка этой задачи как полный; его значение значимо только для переопределения классов setRawResult
. Этот метод может быть полезным, вызывая завершение, как только любой (против всех) нескольких результатов подзадачи получается. Однако то, в общем (и рекомендуемый) случай, в который setRawResult
не переопределяется, этот эффект может быть получен, более просто используя quietlyCompleteRoot();
.
complete
в class ForkJoinTask<T>
rawResult
- необработанный результатpublic final CountedCompleter<?> firstComplete()
null
. Этот метод разрабатывается, чтобы использоваться с nextComplete()
в циклах обхода завершения.null
public final CountedCompleter<?> nextComplete()
ForkJoinTask.quietlyComplete()
и возвраты null
. Или, если количество этой задачи на ожидании является ненулевым, постепенно уменьшает его количество на ожидании и возвраты null
. Иначе, возвращает completer. Этот метод может использоваться в качестве части цикла обхода завершения для гомогенных иерархий задач:
for (CountedCompleter<?> c = firstComplete();
c != null;
c = c.nextComplete()) {
// ... process c ...
}
null
если ни одинpublic final void quietlyCompleteRoot()
getRoot().quietlyComplete()
.protected final boolean exec()
exec
в class ForkJoinTask<T>
true
если эта задача, как известно, обычно завершаласьpublic T getRawResult()
null
, который является подходящим для Void
действия, но в других случаях должны быть переопределены, почти всегда, чтобы возвратить поле или функцию поля, которое содержит результат после завершения.getRawResult
в class ForkJoinTask<T>
protected void setRawResult(T t)
setRawResult
в class ForkJoinTask<T>
t
- значение
Для дальнейшей ссылки API и документации разработчика, см. Java Документация SE. Та документация содержит более подробные, предназначенные разработчиком описания, с концептуальными краткими обзорами, определениями сроков, обходных решений, и рабочих примеров кода.
Авторское право © 1993, 2013, Oracle и/или его филиалы. Все права защищены.
Проект сборка-b92