Параллельные потоки Java 8: основные советы

Java задняя часть JVM

Параллельные потоки 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