Параллельные потоки Java 8 используют общий пул потоков, что сильно влияет на производительность. Вы можете обернуть потоки, чтобы вызвать собственный пул потоков для решения проблем с производительностью.
проблема
Параллельные потоки Java 8 позволяют нам относительно легко выполнять параллельные задачи.
myList.parallelStream.map(obj -> longRunningOperation())
Но у этого есть серьезная проблема: на фоне JVM, используя общий fork/join
Для выполнения вышеуказанных функций пул используется всеми параллельными потоками. По умолчанию fork/join
Пул выделяет один поток на процессор. Допустим, у вас есть машина с 16 ядрами, поэтому вы можете создать только 16 потоков. к процессору
Для интенсивных задач это имеет смысл, поскольку ваша машина действительно может выполнять только 16 потоков. Но на самом деле не все задачи интенсивно используют ЦП. Например:
myList.parallelStream
.map(this::retrieveFromA)
.map(this::processUsingB)
.forEach(this::saveToC)
myList.parallelStream
.map(this::retrieveFromD)
.map(this::processUsingE)
.forEach(this::saveToD)
Эти два потока в значительной степени ограничены операциями ввода-вывода, поэтому они будут ждать других систем. Но два потока используют один и тот же (небольшой) пул потоков и, таким образом, блокируются, ожидая друг друга. Это очень плохо, и это можно исправить. В качестве примера возьмем поток:
final List<Integer> firstRange = buildIntRange();
firstRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
Полный код можно найти наgistПосмотреть выше.
Во время выполнения я получаю файл дампа потока. Вот соответствующая тема (на моем Macbook):
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
Теперь я хочу выполнить эти два параллельных потока параллельно.
Runnable firstTask = () -> {
firstRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
};
Runnable secondTask = () -> {
secondRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
};
// run threads
Полный код можно найти наgistПосмотреть выше.
На этот раз мы снова смотрим на файл дампа потока:
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
Как видите, результат тот же. Мы использовали только 4 потока.
обходной путь
Как я уже упоминал, JVM за кулисами использует пул fork/join, и в документации ForkJoinTask мы можем увидеть:
При необходимости запланируйте асинхронно выполняемую задачу для текущего работающего пула. Если задача не находится в inForkJoinPool(), вы также можете вызвать ForkJoinPool.commonPool(), чтобы получить новый пул для выполнения.
дай мне попробовать...
ForkJoinPool forkJoinPool = new ForkJoinPool(3);
forkJoinPool.submit(() -> {
firstRange.parallelStream().forEach((number) -> {
try {
Thread.sleep(5);
} catch (InterruptedException e) { }
});
});
ForkJoinPool forkJoinPool2 = new ForkJoinPool(3);
forkJoinPool2.submit(() -> {
secondRange.parallelStream().forEach((number) -> {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
});
});
Полный код можно найти наgistПосмотреть выше.
Теперь давайте снова посмотрим на пул потоков:
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-4
ForkJoinPool-2-worker-1
ForkJoinPool-2-worker-2
ForkJoinPool-2-worker-3
ForkJoinPool-1-worker-4
Поскольку мы создаем собственный пул потоков, мы можем избежать общих пулов потоков и даже при необходимости выделить больше потоков, чем количество процессоров.
ForkJoinPool forkJoinPool = new ForkJoinPool(<numThreads>);
Добро пожаловать в колонку"Идти в ногу с Java 8", делитесь превосходными руководствами и учебными пособиями по китайскому языку Java8 и приветствуйте высококачественные статьи.
Оригинальная ссылка:tobyhobsonперевести: ImportNew.com - paddx
Ссылка на перевод:www.importnew.com/16801.html