
package me.whiteship.chapter07.item48;
import java.math.BigInteger;
import java.util.stream.Stream;
import static java.math.BigInteger.ONE;
import static java.math.BigInteger.TWO;
// 병렬 스트림을 사용해 처음 20개의 "메르센 소수"를 생성하는 프로그램 (291쪽 코드 48-1의 병렬화 버전)
// 주의: 병렬화의 영향으로 프로그램이 종료하지 않는다.
public class ParallelMersennePrimes {
public static void main(String[] args) {
primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
.parallel() // 스트림 병렬화
.filter(mersenne -> mersenne.isProbablePrime(50))
.limit(20)
.forEach(System.out::println);
}
static Stream<BigInteger> primes() {
return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}
}
limit이나 iterate를 Serializable이 필요하기 때문에 parallel이 적절하지 않다.
limit은 전체를 순환한 후에 가져오기 때문에 너무 느리다.
병렬적으로 분해되는 데이터 소스가 아니다.
그래서 parallel을 잘못 쓰면 더 느려진다.
array는 데이터가 모여있어서 가장 빠르다.
int의 범위, long 범위를 쓸 때 최적화 하기 좋다.
중간 연산에는 결합법칙에 만족해야 하고 간섭받지 말아야 한다.
결합법칙은
// (a op b) op c == a op (b op c)
// (a + b) + c == a + (b + c)
// (a - b) - c != a - (b - c)
파이프라인을 수행하는 도중에 데이터가 변경되지 말아야 한다.
collect는 결과에 해당하는 결과값이 바뀌는 것이다.
package me.whiteship.chapter07.item48;
import java.math.BigInteger;
import java.util.stream.LongStream;
public class ParallelPrimeCounting {
// 코드 48-3 소수 계산 스트림 파이프라인 - 병렬화 버전 (295쪽)
static long pi(long n) {
return LongStream.rangeClosed(2, n)
.parallel()
.mapToObj(BigInteger::valueOf)
.filter(i -> i.isProbablePrime(50))
.count();
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(start);
System.out.println(pi(10_000_000));
System.out.println(System.currentTimeMillis() - start);
}
}
2부터 100만까지는 적절한 범위이다.
count는 적절한 종단 연산이다.


Spliterator == Splitting + Iterator

package me.whiteship.chapter07.item48.spliterator;
import java.util.Arrays;
import java.util.List;
import java.util.Spliterator;
public class SpliteratorExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
Spliterator<String> spliterator = list.spliterator();
System.out.println("spliterator.tryAdvance:");
spliterator.tryAdvance(System.out::println);
System.out.println("spliterator.forEachRemaining:");
spliterator.forEachRemaining(System.out::println);
}
}
tryAdvance는 뒤에 있는 함수를 한번만 적용해보는 것이다.
그 다음부터 처리하고 싶으면 banana부터 출력이 된다.

package me.whiteship.chapter07.item48.spliterator;
import java.util.Arrays;
import java.util.List;
import java.util.Spliterator;
public class SpliteratorTrySplitExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape");
Spliterator<String> spliterator1 = list.spliterator();
Spliterator<String> spliterator2 = spliterator1.trySplit();
System.out.println("Spliterator 1:");
spliterator1.forEachRemaining(System.out::println);
System.out.println("Spliterator 2:");
if (spliterator2 != null) {
spliterator2.forEachRemaining(System.out::println);
}
}
}

trySplit일 때 null이 나올 수도 있다.
package me.whiteship.chapter07.item48.spliterator;
import java.util.Arrays;
import java.util.List;
import java.util.Spliterator;
public class SpliteratorCharacteristicsExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
Spliterator<String> spliterator = list.spliterator();
// Check and print characteristics
int characteristics = spliterator.characteristics();
System.out.println("Characteristics: " + characteristics);
// System.out.println("Characteristics: " + Integer.toBinaryString(characteristics));
// Check if the Spliterator is ORDERED
if ((characteristics & Spliterator.ORDERED) != 0) {
System.out.println("Spliterator is ORDERED");
}
// if (spliterator.hasCharacteristics(Spliterator.ORDERED)) {
// System.out.println("Spliterator is ORDERED");
// }
// Check if the Spliterator is DISTINCT
if ((characteristics & Spliterator.DISTINCT) != 0) {
System.out.println("Spliterator is DISTINCT");
}
// Check if the Spliterator is SORTED
if ((characteristics & Spliterator.SORTED) != 0) {
System.out.println("Spliterator is SORTED");
}
// Check if the Spliterator is SIZED
if ((characteristics & Spliterator.SIZED) != 0) {
System.out.println("Spliterator is SIZED");
}
// Check if the Spliterator is NONNULL
if ((characteristics & Spliterator.NONNULL) != 0) {
System.out.println("Spliterator is NONNULL");
}
// Check if the Spliterator is IMMUTABLE
if ((characteristics & Spliterator.IMMUTABLE) != 0) {
System.out.println("Spliterator is IMMUTABLE");
}
// Check if the Spliterator is CONCURRENT
if ((characteristics & Spliterator.CONCURRENT) != 0) {
System.out.println("Spliterator is CONCURRENT");
}
// Check if the Spliterator is SUBSIZED
if ((characteristics & Spliterator.SUBSIZED) != 0) {
System.out.println("Spliterator is SUBSIZED");
}
}
}
Charateristic은 가져와서 객체의 특징을 가지고 오는 것이다.

parallel에서 spliter를 사용하고 있다. 그리고 이러한 메소드들이 있다.

package me.whiteship.chapter07.item48.random;
import java.util.Random;
import java.util.SplittableRandom;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;
public class RandomExample {
public static void main(String[] args) {
// Using Random
Random random = new Random();
System.out.println("Random: " + random.nextInt(100));
// Using ThreadLocalRandom
int randomValue = ThreadLocalRandom.current().nextInt(100);
System.out.println("ThreadLocalRandom: " + randomValue);
// Using SplittableRandom
SplittableRandom splittableRandom = new SplittableRandom();
IntStream randomIntStream = splittableRandom.ints(10, 0, 100);
long count = randomIntStream
.parallel()
.filter(number -> number > 50)
.count();
System.out.println(count);
}
}
멀티스레드 환경에서는 Random이 적합하지는 않다. threadsafe는 해서 문제는 없는데 성능 상 안좋다.
synchronized 되어있어서 느리다.
멀티스레드에서는 ThreadLocalRandom이 좋다.
splittableRandom은 threadsafe하지 않다. stream을 만들어서 병렬처리를 하기 위한 random이다.