Post

๐Ÿน chap7. ๋ณ‘๋ ฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ์„ฑ๋Šฅ

7.1 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ

: ๊ฐ๊ฐ์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์—ฌ๋Ÿฌ ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•œ ์ŠคํŠธ๋ฆผ์ด๋‹ค. ๋ชจ๋“  ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ”„๋กœ์„ธ์„œ๊ฐ€ ๊ฐ๊ฐ์˜ ์ฒญํฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.

์ปฌ๋ ‰์…˜์— parallelStream์„ ํ˜ธ์ถœํ•˜๋ฉด ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์ด ์ƒ์„ฑ๋œ๋‹ค.

ex) ์ˆซ์ž n์„ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ 1๋ถ€ํ„ฐ n๊นŒ์ง€์˜ ๋ชจ๋“  ์ˆซ์ž์˜ ํ•ฉ๊ณ„๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ ๊ตฌํ˜„

1
2
3
4
5
public long sequentialSum(long n) {
    return Stream.iterate(1L, i -> i + 1) // ๋ฌดํ•œ ์ž์—ฐ์ˆ˜ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
                 .limit(n) // n๊ฐœ ์ดํ•˜๋กœ ์ œํ•œ
                 .reduce(0L, Long::sum); // ๋ชจ๋“  ์ˆซ์ž๋ฅผ ๋”ํ•˜๋Š” ์ŠคํŠธ๋ฆผ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ
}

์ „ํ†ต์ ์ธ ์ž๋ฐ”์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
public long iterativeSum(long n) {
    long result = 0;
    for (long i = 1L; i <= n; i++) {
        result += i;
    }
    return result;
}

ํŠนํžˆ n์ด ์ปค์ง„๋‹ค๋ฉด ์ด ์—ฐ์‚ฐ์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ด๋‹ค.

  • ๋ฌด์—‡๋ถ€ํ„ฐ ๊ฑด๋“œ๋ ค์•ผํ•˜๋‚˜?
  • ๊ฒฐ๊ณผ ๋ณ€์ˆ˜๋Š” ์–ด๋–ป๊ฒŒ ๋™๊ธฐํ™”ํ•ด์•ผ ํ•˜๋‚˜?
  • ๋ช‡ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‚˜?
  • ์ˆซ์ž๋Š” ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•  ๊ฒƒ์ธ๊ฐ€?
  • ์ƒ์„ฑ๋œ ์ˆซ์ž๋Š” ๋ˆ„๊ฐ€ ๋”ํ•˜๋‚˜?

๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋ฉด ์œ„ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

7.1.1 ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์— parallel ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ธฐ์กด์˜ ํ•จ์ˆ˜ํ˜• ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ(์ˆซ์ž ํ•ฉ๊ณ„ ๊ณ„์‚ฐ)์ด ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค.

1
2
3
4
5
public long parallelSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
                 .limit(n)
                 **.parallel()** // ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜
                 .reduce(0L, Long::sum);

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ์ŠคํŠธ๋ฆผ์ด ์—ฌ๋Ÿฌ ์ฒญํฌ๋กœ ๋ถ„ํ• ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์—ฌ๋Ÿฌ ์ฒญํฌ์— ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์œผ๋กœ ์ƒ์„ฑ๋œ ๋ถ€๋ถ„ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์œผ๋กœ ํ•ฉ์ณ์„œ ์ „์ฒด ์ŠคํŠธ๋ฆผ์˜ ๋ฆฌ๋“€์‹ฑ ๊ฒฐ๊ณผ๋ฅผ ๋„์ถœํ•œ๋‹ค.

์‚ฌ์‹ค ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์— parallel์„ ํ˜ธ์ถœํ•ด๋„ ์ŠคํŠธ๋ฆผ ์ž์ฒด์—๋Š” ์•„๋ฌด ๋ณ€ํ™”๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” parallel์„ ํ˜ธ์ถœํ•˜๋ฉด ์ดํ›„ ์—ฐ์‚ฐ์ด ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•˜๋Š” ๋ถˆ๋ฆฌ์–ธ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์„ค์ •๋œ๋‹ค.

๋ฐ˜๋Œ€๋กœ sequential๋กœ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ์ด ๋‘ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์–ด๋–ค ์—ฐ์‚ฐ์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๊ณ  ์–ด๋–ค ์—ฐ์‚ฐ์„ ์ˆœ์ฐจ๋กœ ์‹คํ–‰ํ• ์ง€ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
stream.parallel()
    .filter(...)
    .sequential()
    .map(...)
    .parallel()
    .reduce();

parallel๊ณผ sequential ๋‘ ๋ฉ”์„œ๋“œ ์ค‘ ์ตœ์ข…์ ์œผ๋กœ ํ˜ธ์ถœ๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์— ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค.

์œ„ ์ฝ”๋“œ์—์„œ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ์€ parallel์ด๋ฏ€๋กœ ํŒŒ์ดํ”„๋ผ์ธ์€ ์ „์ฒด์ ์œผ๋กœ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋œ๋‹ค.

7.1.2 ์ŠคํŠธ๋ฆผ ์„ฑ๋Šฅ ์ธก์ •

๋ณ‘๋ ฌํ™”๋ฅผ ์ด์šฉํ•˜๋ฉด ์ˆœ์ฐจ๋‚˜ ๋ฐ˜๋ณต ํ˜•์‹์— ๋น„ํ•ด ์„ฑ๋Šฅ์ด ์ข‹์•„์งˆ ๊ฒƒ์ด๋ผ ์ถ”์ธกํ•˜์ง€๋งŒ, ์‹ค์ œ ์ธก์ •์„ ํ•ด๋ด์•ผํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์ž๋ฐ” ๋งˆ์ดํฌ๋กœ๋ฒค์น˜๋งˆํฌ ํ•˜๋‹ˆ์Šค(JMH)๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์ž‘์€ ๋ฒค์น˜๋งˆํฌ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. JMH๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ณ , ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๋ฐฉ์‹์„ ์ง€์›ํ•˜๋ฉฐ, ์•ˆ์ •์ ์œผ๋กœ ์ž๋ฐ” ํ”„๋กœ๊ทธ๋žจ์ด๋‚˜ ์ž๋ฐ” ๊ฐ€์ƒ ๋จธ์‹ (JVM)์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์šฉ ๋ฒค์น˜๋งˆํฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฉ”์ด๋ธ(Maven) ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋นŒ๋“œ ๊ณผ์ •์„ ์ •์˜ํ•˜๋Š” pom.xml ํŒŒ์ผ์— ๋ช‡ ๊ฐ์ง€ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด ํ”„๋กœ์ ํŠธ์—์„œ JMH๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jml-core</artifactId>
  <version>1.17.4</version>
</dependency>
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jmh-generator-annprocess</artifactId>
  <version>1.17.4</version>
</dependency>

์ฒซ ๋ฒˆ์งธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ•ต์‹ฌ JMH ๊ตฌํ˜„์„ ํฌํ•จํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ž๋ฐ” ์•„์นด์ด๋ธŒ(Java Archive, JAR) ํŒŒ์ผ์„ ๋งŒ๋“œ๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ๋ฅผ ํฌํ•จํ•œ๋‹ค.

๋ฉ”์ด๋ธ ์„ค์ •์— ๋‹ค์Œ ํ”Œ๋Ÿฌ๊ทธ์ธ๋„ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ์ž๋ฐ” ์•„์นด์ด๋ธŒ ํŒŒ์ผ์„ ์ด์šฉํ•ด์„œ ๋ฒค์น˜๋งˆํฌ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<build>
  <plugins>
	<plugin>
	  <groupId>org.apache.maven.plugins</groupId>
	  <artifactId>maven-shade-plugin</artifactId>
	  <executions>
		<execution>
		  <phase>package</phase>
		  <goals><goal>shade</goal></goals>
		  <configuration>
			<finalName>benchmarks</finalName>
			<transformers>
			  <transformer implementation=
				"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
				<mainClass>org.openjdk.jmh.Main</mainClass>
			  </transformer>
			</transformers>
		  </configuration>
		</execution>
	  </executions>
	</plugin>
  </plugins>
</build>

์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ด์ „์˜ sequentialSum ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฒค์น˜๋งˆํฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • n๊ฐœ์˜ ์ˆซ์ž๋ฅผ ๋”ํ•˜๋Š” ํ•จ์ˆ˜์˜ ์„ฑ๋Šฅ ์ธก์ •
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@BenchmarkMode(Mode.AverageTime) // ๋ฒค์น˜๋งˆํฌ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฐ ํ‰๊ท  ์‹œ๊ฐ„ ์ธก์ •
@OutputTimeUnit(TimeUnit.MILLISECONS) // ๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„๋กœ ์ถœ๋ ฅ
@Fork(2, jvmArgs={"-Xms4G", "-Xmx4G"}) // 4Gb์˜ ํž™ ๊ณต๊ฐ„์„ ์ œ๊ณตํ•œ ํ™˜๊ฒฝ์—์„œ ๋‘ ๋ฒˆ ๋ฒค์น˜๋งˆํฌ๋ฅผ ์ˆ˜ํ–‰ํ•ด ๊ฒฐ๊ณผ์˜ ์‹ ๋ขฐ์„ฑ ํ™•๋ณด
public class ParallelStreamBenchmark {
    private static final long N = 10_000_000L;

    @Benchmark // ๋ฒค์น˜๋งˆํฌ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ
    public long sequentialSum() {
        return Stream.iterate(1L, i -> i + 1).limit(N)
                     .reduce(0L, Long::sum);
    }

    @TearDown(Level.Invocation) // ๋งค ๋ฒˆ ๋ฒค์น˜๋งˆํฌ๋ฅผ ์‹คํ–‰ํ•œ ๋‹ค์Œ์—๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ ๋™์ž‘ ์‹œ๋„
    public void tearDown() {
        System.gc();
    }
}

ํด๋ž˜์Šค๋ฅผ ์ปดํŒŒ์ผํ•˜๋ฉด ์ด์ „์— ์„ค์ •ํ•œ ๋ฉ”์ด๋ธ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด benchmarks.jar๋ผ๋Š” ๋‘ ๋ฒˆ์งธ ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค. ๋‹ค์Œ์ฒ˜๋Ÿผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
java -jar ./target/benchmarks.jar ParallelStreamBenchmark 

โ†’ ๋ฒค์น˜๋งˆํฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋„๋ก ํž™์˜ ํฌ๊ธฐ๋ฅผ ์ถฉ๋ถ„ํ•˜๊ฒŒ ์„ค์ •

โ†’ ๋ฒค์น˜๋งˆํฌ๊ฐ€ ๋๋‚  ๋•Œ๋งˆ๋‹ค ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ๊ฐ•์ œ

์ด๋ ‡๊ฒŒ ํ•ด๋„ ๊ฒฐ๊ณผ๋Š” ์ •ํ™•ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. (๊ธฐ๊ณ„๊ฐ€ ์ง€์›ํ•˜๋Š” ์ฝ”์–ด์˜ ๊ฐœ์ˆ˜ ๋“ฑ์ด ์‹คํ–‰ ์‹œ๊ฐ„์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • ์œ„ ์ฝ”๋“œ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ํšŸ์ˆ˜
    • ๋ฒค์น˜๋งˆํฌ ์ค€๋น„: JMH ๋ช…๋ น์€ ํ•ซ์ŠคํŒŸ์ด ์ฝ”๋“œ๋ฅผ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋„๋ก 20๋ฒˆ ์‹คํ–‰
    • ์ตœ์ข… ๊ฒฐ๊ณผ ๊ณ„์‚ฐ: 20๋ฒˆ ๋” ์‹คํ–‰

    JMH๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 20+20ํšŒ ํ”„๋กœ๊ทธ๋žจ์„ ๋ฐ˜๋ณต ์‹คํ–‰

    (ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์ด๋‚˜ -w, -i ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ช…๋ นํ–‰์— ์ถ”๊ฐ€ํ•ด์„œ ์ด ๊ธฐ๋ณธ ๋™์ž‘ ํšŸ์ˆ˜๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.)

๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ

๋‹ค์Œ์€ ์ธํ…” i7-4600U 2.1GHz ์ฟผ๋“œ ์ฝ”์–ด์—์„œ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ „ํ†ต์ ์ธ for ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ˜๋ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋” ์ €์ˆ˜์ค€์œผ๋กœ ๋™์ž‘ํ•  ๋ฟ ์•„๋‹ˆ๋ผ ํŠนํžˆ ๊ธฐ๋ณธ๊ฐ’์„ ๋ฐ•์‹ฑํ•˜๊ฑฐ๋‚˜ ์–ธ๋ฐ•์‹ฑํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋” ๋น ๋ฅผ ๊ฒƒ์ด๋ผ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
@Benchmark
public long iterativeSum() {
    long result = 0;
    for (long i = 1L; i <= N; i++) {
        result += i;
    }
    return result;
}

๊ฐ™์€ ์ปดํ“จํ„ฐ๋กœ ๋‘ ๋ฒˆ์งธ ๋ฒค์น˜๋งˆํฌ ์ฝ”๋“œ๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ์ด๋‹ค.

์˜ˆ์ƒ๋Œ€๋กœ ์ˆœ์ฐจ์ ์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„์ „์— ๋น„ํ•ด ๊ฑฐ์˜ 4๋ฐฐ๊ฐ€ ๋น ๋ฅด๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒˆ์—๋Š” ๋ฒค์น˜๋งˆํฌ ํด๋ž˜์Šค์— ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„์ „์˜ ๊ฒฐ๊ณผ๋‹ค.

๋ณ‘๋ ฌ ๋ฒ„์ „์ด ์ฟผ๋“œ ์ฝ”์–ด CPU๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ณ  ์ˆœ์ฐจ ๋ฒ„์ „์— ๋น„ํ•ด ๋‹ค์„ฏ ๋ฐฐ๋‚˜ ๋Š๋ฆฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”๋‹ค. ์ด ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฐ˜๋ณต ๊ฒฐ๊ณผ๋กœ ๋ฐ•์‹ฑ๋œ ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ ์ˆซ์ž๋ฅผ ๋”ํ•˜๋ ค๋ฉด ์–ธ๋ฐ•์‹ฑ์„ ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฐ˜๋ณต ์ž‘์—…์€ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋…๋ฆฝ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.

์•„๋ž˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์ด์ „ ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋‹ค์Œ ํ•จ์ˆ˜์˜ ์ž…๋ ฅ์ด ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— iterate ์—ฐ์‚ฐ์„ ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.

๋ฆฌ๋“€์‹ฑ ๊ณผ์ •์„ ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์— ์ „์ฒด ์ˆซ์ž ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์—†๋‹ค. ์ŠคํŠธ๋ฆผ์ด ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌ๋˜๋„๋ก ์ง€์‹œํ–ˆ๊ณ  ๊ฐ๊ฐ์˜ ํ•ฉ๊ณ„๊ฐ€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰๋˜์—ˆ์ง€๋งŒ ๊ฒฐ๊ตญ ์ˆœ์ฐจ์ฒ˜๋ฆฌ ๋ฐฉ์‹๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅธ ์ ์ด ์—†์œผ๋ฏ€๋กœ ์Šค๋ ˆ๋“œ๋ฅผ ํ• ๋‹นํ•˜๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๋งŒ ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋œ๋‹ค.

๋ณ‘๋ ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์˜ค์šฉํ•˜๋ฉด ์˜คํžˆ๋ ค ์ „์ฒด ํ”„๋กœ๊ทธ๋žจ์˜ ์„ฑ๋Šฅ์ด ๋” ๋‚˜๋น ์งˆ ์ˆ˜๋„ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ parallel ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ๊ผญ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

๋” ํŠนํ™”๋œ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

LongStream.rangeClosed๋ผ๋Š” ๋ฉ”์„œ๋“œ๋Š” iterate์— ๋น„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์„ ์ œ๊ณตํ•œ๋‹ค.

  • LongStream.rangeClosed๋Š” ๊ธฐ๋ณธํ˜• long์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ฐ•์‹ฑ๊ณผ ์–ธ๋ฐ•์‹ฑ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค.
  • LongStream.rangeClosed๋Š” ์‰ฝ๊ฒŒ ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆซ์ž ๋ฒ”์œ„๋ฅผ ์ƒ์‚ฐํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด 1-20 ๋ฒ”์œ„์˜ ์ˆซ์ž๋ฅผ ๊ฐ๊ฐ 1-5, 6-10, 11-15, 16-20 ๋ฒ”์œ„์˜ ์ˆซ์ž๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋‹ค.

์–ธ๋ฐ•์‹ฑ๊ณผ ๊ด€๋ จํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๋Š” ์–ผ๋งˆ๋‚˜ ๋ ๊นŒ? ์šฐ์„  ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹œ๊ฐ„์„ ์ธก์ •ํ•œ๋‹ค.

1
2
3
4
5
@Benchmark
public long rangedSum() {
    return LongStream.rangeClosed(1, N)
                     .reduce(0L, Long::sum);
}

๊ธฐ์กด์˜ iterate ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ ์ƒ์„ฑํ•œ ์ˆœ์ฐจ ๋ฒ„์ „์— ๋น„ํ•ด ์ด ์˜ˆ์ œ์˜ ์ˆซ์ž ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋” ๋น ๋ฅด๋‹ค. ํŠนํ™”๋˜์ง€ ์•Š์€ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” ์˜คํ† ๋ฐ•์‹ฑ, ์–ธ๋ฐ•์‹ฑ ๋“ฑ์˜ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ˆ˜๋ฐ˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ๋Š” ์–ด๋–ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ ์ ˆํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•˜๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋‹จ์ ์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค. ์ด๋ฒˆ์—” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์€ ์–ด๋–จ๊นŒ?

1
2
3
4
5
6
@Benchmark
public long parallelRangedSum() {
    return LongStream.rangedClosed(1, N)
                     .parallel()
                     .reduce(0L, Long::sum);
}

์ด๋ฒˆ์—” ์‹ค์งˆ์ ์œผ๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์ด ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•ด์•ผ ๋ณ‘๋ ฌ ์‹คํ–‰๋„ ์ตœ์ ์˜ ๊ธฐ๋Šฅ์„ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์˜ฌ๋ฐ”๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋น„ํ•ด ์ตœ์‹  ๋ฉ€ํ‹ฐ์ฝ”์–ด CPU๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ณ‘๋ ฌ ์‹คํ–‰์˜ ํž˜์„ ๋‹จ์ˆœํ•˜๊ฒŒ ์ง์ ‘์ ์œผ๋กœ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

But ๋ณ‘๋ ฌํ™”๊ฐ€ ์™„์ „ ๊ณต์งœ๋Š” ์•„๋‹ˆ๋‹ค. ๋ณ‘๋ ฌํ™”๋ฅผ ์ด์šฉํ•˜๋ ค๋ฉด

  1. ์ŠคํŠธ๋ฆผ์„ ์žฌ๊ท€์ ์œผ๋กœ ๋ถ„ํ• ํ•ด์•ผ ํ•˜๊ณ 
  2. ๊ฐ ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์„ ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์œผ๋กœ ํ• ๋‹นํ•˜๊ณ 
  3. ์ด๋“ค ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ํ•ฉ์ณ์•ผ ํ•œ๋‹ค.

๋ฉ€ํ‹ฐ์ฝ”์–ด ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ์ด๋™์€ ์ƒ๊ฐ๋ณด๋‹ค ๋น„์‹ธ๋‹ค. ๋”ฐ๋ผ์„œ ์ฝ”์–ด ๊ฐ„์— ๋ฐ์ดํ„ฐ ์ „์†ก ์‹œ๊ฐ„๋ณด๋‹ค ํ›จ์”ฌ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…๋งŒ ๋ณ‘๋ ฌ๋กœ ๋‹ค๋ฅธ ์ฝ”์–ด์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.

7.1.3 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ•

๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•˜๋Š” ๋งŽ์€ ๋ฌธ์ œ๋Š” ๊ณต์œ ๋œ ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ์–ด๋‚œ๋‹ค. ๋‹ค์Œ์€ n๊นŒ์ง€์˜ ์ž์—ฐ์ˆ˜๋ฅผ ๋”ํ•˜๋ฉด์„œ ๊ณต์œ ๋œ ๋ˆ„์ ์ž๋ฅผ ๋ฐ”๊พธ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
public long sideEffectSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).forEach(accumulator::add);
    return accumulator.total;
}

public class Accumulator {
    public long total = 0;
    public void add(long value) { total += value; }
}

๋ˆ„์ ์ž๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์”ฉ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ๋ˆ„์ ์ž์— ์ˆซ์ž๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„ ์ฝ”๋“œ๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ์ˆœ์ฐจ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ฐธ์‚ฌ๊ฐ€ ์ผ์–ด๋‚œ๋‹ค. ํŠนํžˆ total์„ ์ ‘๊ทผํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ๋ฌธ์ œ(๋‹ค์ˆ˜์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š”) ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚œ๋‹ค. ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋‹ค๋ณด๋ฉด ๊ฒฐ๊ตญ ๋ณ‘๋ ฌํ™”๋ผ๋Š” ํŠน์„ฑ์ด ์—†์–ด์ ธ ๋ฒ„๋ฆด ๊ฒƒ์ด๋‹ค.

1
2
3
4
5
public long sideEffectParallelSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
    return accumulator.total;
}
1
2
System.out.println("SideEffect parallel sum done in: " +
    measurePerf(ParallelStreams::sideEffectParallelSum, 10_000_000L) + " msecs" );

๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

๋ฉ”์„œ๋“œ์˜ ์„ฑ๋Šฅ์€ ๋‘˜์งธ ์น˜๊ณ , ์˜ฌ๋ฐ”๋ฅธ ๊ฒฐ๊ณผ๊ฐ’(50000005000000)์ด ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค. ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ๋ˆ„์ ์ž, ์ฆ‰ total += value๋ฅผ ์‹คํ–‰ํ•˜๋ฉด์„œ ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๊ณต์œ ํ•˜๋Š” ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๋Š” forEach ๋ธ”๋ก ๋‚ด๋ถ€์—์„œ add ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ด ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ƒํƒœ ๊ณต์œ ์— ๋”ฐ๋ฅธ ๋ถ€์ž‘์šฉ์„ ํ”ผํ•ด์•ผ ํ•œ๋‹ค.

7.1.4 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํ™•์‹ ์ด ์„œ์ง€ ์•Š์œผ๋ฉด ์ง์ ‘ ์ธก์ •ํ•˜๋ผ. ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์‰ฝ๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ์ ์ ˆํ•œ ๋ฒค์น˜๋งˆํฌ๋กœ ์ง์ ‘ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.
  • ๋ฐ•์‹ฑ์„ ์ฃผ์˜ํ•˜๋ผ. ์ž๋™ ๋ฐ•์‹ฑ๊ณผ ์–ธ๋ฐ•์‹ฑ์€ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์š”์†Œ๋‹ค. ๋˜๋„๋ก์ด๋ฉด ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ(IntStream, LongStream, DoubleStream)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ๋ณด๋‹ค ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์—์„œ ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง€๋Š” ์—ฐ์‚ฐ์ด ์žˆ๋‹ค. ํŠนํžˆ limit๋‚˜ findFirst์ฒ˜๋Ÿผ ์š”์†Œ์˜ ์ˆœ์„œ์— ์˜์กดํ•˜๋Š” ์—ฐ์‚ฐ์€ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์ˆ˜ํ–‰ ์‹œ ๋น„์‹ผ ๋น„์šฉ์„ ์น˜๋Ÿฌ์•ผ ํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด firstAny๋Š” ์š”์†Œ์˜ ์ˆœ์„œ์™€ ์ƒ๊ด€์—†์ด ์—ฐ์‚ฐํ•˜๋ฏ€๋กœ findFirst๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹๋‹ค. ์ •๋ ฌ๋œ ์ŠคํŠธ๋ฆผ์— unordered๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋น„ ์ •๋ ฌ๋œ ์ŠคํŠธ๋ฆผ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ์ŠคํŠธ๋ฆผ์— N๊ฐœ์˜ ์š”์†Œ๊ฐ€ ์žˆ์„ ๋•Œ ์š”์†Œ์˜ ์ˆœ์„œ๊ฐ€ ์ƒ๊ด€์—†๋‹ค๋ฉด ๋น„์ •๋ ฌ๋œ ์ŠคํŠธ๋ฆผ์— limit์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ด๋‹ค.

  • ์ŠคํŠธ๋ฆผ์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์—ฐ์‚ฐ ๋น„์šฉ์„ ๊ณ ๋ คํ•˜๋ผ.

    ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์š”์†Œ ์ˆ˜๊ฐ€ N์ด๊ณ  ํ•˜๋‚˜์˜ ์š”์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋“œ๋Š” ๋น„์šฉ์ด Q๋ผํ•˜๋ฉด N*Q๋กœ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. Q๊ฐ€ ๋†’๋‹ค๋Š” ๊ฒƒ์€ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

  • ์†Œ๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ์—์„œ๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์ด ๋„์›€ ๋˜์ง€ ์•Š๋Š”๋‹ค.

    ๋ณ‘๋ ฌํ™” ๊ณผ์ •์—์„œ ์ƒ๊ธฐ๋Š” ๋ถ€๊ฐ€ ๋น„์šฉ์„ ์ƒ์‡„ํ•  ๋งŒํผ์˜ ์ด๋“์„ ์–ป์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • ์ŠคํŠธ๋ฆผ์„ ๊ตฌ์„ฑํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ์ ์ ˆํ•œ์ง€ ํ™•์ธํ•ด๋ผ

    ์˜ˆ๋ฅผ ๋“ค์–ด ArrayList๋ฅผ LinkedList๋ณด๋‹ค ํšจ์œจ์ ์œผ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋‹ค. LinkedList๋ฅผ ๋ถ„ํ• ํ•˜๋ ค๋ฉด ๋ชจ๋“  ์š”์†Œ๋ฅผ ํƒ์ƒ‰ํ•ด์•ผ ํ•˜์ง€๋งŒ ArrayList๋Š” ์š”์†Œ๋ฅผ ํƒ์ƒ‰ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

    ๋˜ํ•œ range ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“  ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ๋„ ์‰ฝ๊ฒŒ ๋ถ„ํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ŠคํŠธ๋ฆผ์˜ ํŠน์„ฑ๊ณผ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ด ์ŠคํŠธ๋ฆผ์˜ ํŠน์„ฑ์„ ์–ด๋–ป๊ฒŒ ๋ฐ”๊พธ๋Š”์ง€์— ๋”ฐ๋ผ ๋ถ„ํ•ด ๊ณผ์ •์˜ ์„ฑ๋Šฅ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด SIZED ์ŠคํŠธ๋ฆผ์€ ์ •ํ™•ํžˆ ๊ฐ™์€ ํฌ๊ธฐ์˜ ๋‘ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํšจ๊ณผ์ ์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•„ํ„ฐ ์—ฐ์‚ฐ์ด ์žˆ์œผ๋ฉด ์ŠคํŠธ๋ฆผ์˜ ๊ธธ์ด๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํšจ๊ณผ์ ์ธ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„์ง€ ์•Œ ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

  • ์ตœ์ข… ์—ฐ์‚ฐ์˜ ๋ณ‘ํ•ฉ ๊ณผ์ • ๋น„์šฉ์„ ์‚ดํŽด๋ณด๋ผ.

    ๋ณ‘ํ•ฉ ๊ณผ์ •์˜ ๋น„์šฉ์ด ๋น„์‹ธ๋‹ค๋ฉด ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์–ป์€ ์„ฑ๋Šฅ์˜ ์ด์ต์ด ์„œ๋ธŒ ์ŠคํŠธ๋ฆผ์˜ ๋ถ€๋ถ„ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” ๊ณผ์ •์—์„œ ์ƒ์‡„๋  ์ˆ˜ ์žˆ๋‹ค.

์†Œ์Šค๋ถ„ํ•ด์„ฑ
ArrayListํ›Œ๋ฅญํ•จ
LinkedList๋‚˜์จ
IntStream.rangeํ›Œ๋ฅญํ•จ
Stream.iterate๋‚˜์จ
HashSet์ข‹์Œ
TreeSet์ข‹์Œ

7.2 ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ

โ†’ ๋ณ‘๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์„ ์žฌ๊ท€์ ์œผ๋กœ ์ž‘์€ ์ž‘์—…์œผ๋กœ ๋ถ„ํ• ํ•œ ๋‹ค์Œ์— ์„œ๋ธŒํƒœ์Šคํฌ ๊ฐ๊ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์ณ์„œ ์ „์ฒด ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค.

ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ์„œ๋ธŒํƒœ์Šคํฌ๋ฅผ ์Šค๋ ˆ๋“œ ํ’€(ForkJoinPool)์˜ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ์— ๋ถ„์‚ฐ ํ• ๋‹นํ•˜๋Š” ExecutorService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

7.2.1 Recursive Task ํ™œ์šฉ

์Šค๋ ˆ๋“œ ํ’€์„ ์ด์šฉํ•˜๋ ค๋ฉด **RecursiveTask**์˜ ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

R : ์€ ๋ณ‘๋ ฌํ™”๋œ ํƒœ์Šคํฌ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ๊ฒฐ๊ณผ ํ˜•์‹ ๋˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์—†์„ ๋•Œ RecursiveAction ํ˜•์‹

RecursiveTask๋ฅผ ์ •์˜ํ•˜๋ ค๋ฉด ์ถ”์ƒ ๋ฉ”์„œ๋“œ compute๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

1
protected abstract R compute();
  • compute ๋ฉ”์„œ๋“œ
    • ํƒœ์Šคํฌ๋ฅผ ์„œ๋ธŒํƒœ์Šคํฌ๋กœ ๋ถ„ํ• ํ•˜๋Š” ๋กœ์ง ์ •์˜
    • ๋” ์ด์ƒ ๋ถ„ํ• ํ•  ์ˆ˜ ์—†์„ ๋•Œ ๊ฐœ๋ณ„ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์‚ฐํ•  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ •์˜

๋”ฐ๋ผ์„œ ๋Œ€๋ถ€๋ถ„์˜ compute ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜์‚ฌ์ฝ”๋“œ ํ˜•์‹์„ ์œ ์ง€ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
if (ํƒœ์Šคํฌ๊ฐ€ ์ถฉ๋ถ„ํžˆ ์ž‘๊ฑฐ๋‚˜ ๋” ์ด์ƒ ๋ถ„ํ• ํ•  ์ˆ˜ ์—†์œผ๋ฉด) {
    ์ˆœ์ฐจ์ ์œผ๋กœ ํƒœ์Šคํฌ ๊ณ„์‚ฐ
} else {
    ํƒœ์Šคํฌ๋ฅผ ๋‘ ์„œ๋ธŒํƒœ์Šคํฌ๋กœ ๋ถ„ํ• 
    ํƒœ์Šคํฌ๊ฐ€ ๋‹ค์‹œ ์„œ๋ธŒํƒœ์Šคํฌ๋กœ ๋ถ„ํ• ๋˜๋„๋ก ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•จ
    ๋ชจ๋“  ์„œ๋ธŒํƒœ์Šคํฌ์˜ ์—ฐ์‚ฐ์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ
    ๊ฐ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์นจ
}

์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋ถ„ํ•  ํ›„ ์ •๋ณต(divide-and-conquer) ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋ณ‘๋ ฌํ™” ๊ณผ์ •์ด๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์˜ ForkJoinSumCalculator ์ฝ”๋“œ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋จผ์ € RecursiveTask๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// RecursiveTask๋ฅผ ์ƒ์†๋ฐ›์•„ ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์‚ฌ์šฉํ•  ํƒœ์Šคํฌ ์ƒ์„ฑ
public class ForkJoinSumCalculator 
extends java.util.concurrent.RecursiveTast<Long> { 
    private final long[] numbers; // ๋”ํ•  ์ˆซ์ž ๋ฐฐ์—ด
    private final int start; // ์ด ์„œ๋ธŒํƒœ์Šคํฌ์—์„œ ์ฒ˜๋ฆฌํ•  ๋ฐฐ์—ด์˜ ์ดˆ๊ธฐ ์œ„์น˜
    private final int end;   // ์ด ์„œ๋ธŒํƒœ์Šคํฌ์—์„œ ์ฒ˜๋ฆฌํ•  ๋ฐฐ์—ด์˜ ์ตœ์ข… ์œ„์น˜
    public static final long THRESHOLD = 10_000; // ์ด ๊ฐ’ ์ดํ•˜์˜ ์„œ๋ธŒํƒœ์Šคํฌ๋Š” ๋” ์ด์ƒ ๋ถ„ํ• ํ•  ์ˆ˜ ์—†๋‹ค. 

    // ๋ฉ”์ธ ํƒœ์Šคํฌ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•  ๊ณต๊ฐœ ์ƒ์„ฑ์ž
    public ForkJoinSumCalculator(long[] numbers) { 
        this(numbers, 0, numbers.length);
    }
    
    // ๋ฉ”์ธ ํƒœ์Šคํฌ์˜ ์„œ๋ธŒํƒœ์Šคํฌ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•  ๋น„๊ณต๊ฐœ ์ƒ์„ฑ์ž
    private ForkJoinSumCalculator(long[] numbers, int start, int end) { 
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() { //RecursiveTask์˜ ์ถ”์ƒ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ
        int length = end - start; // ์ด ํƒœ์Šคํฌ์—์„œ ๋”ํ•  ๋ฐฐ์—ด์˜ ๊ธธ์ด
        if (length <= THRESHOLD) {
            // ๊ธฐ์ค€๊ฐ’๊ณผ ๊ฐ™๊ฑฐ๋‚˜ ์ž‘์œผ๋ฉด ์ˆœ์ฐจ์ ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.
            return computeSequentially();
        }
        
        // ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ์ ˆ๋ฐ˜์„ ๋”ํ•˜๋„๋ก ์„œ๋ธŒ ํƒœ์Šคํฌ ์ƒ์„ฑ
        ForkJoinSumCalculator leftTask =
            new ForkJoinSumCalculator(numbers, start, start + length/2);
        // ForkJoinPool์˜ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋กœ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ ํƒœ์Šคํฌ๋ฅผ ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰
        leftTask.fork();
        // ๋ฐฐ์—ด์˜ ๋‚˜๋จธ์ง€ ์ ˆ๋ฐ˜์„ ๋”ํ•˜๋„๋ก ์„œ๋ธŒํƒœ์Šคํฌ ์ƒ์„ฑ
        ForkJoinSumCalculator rightTask = 
            new ForkJoinSumCalculator(numbers, start + length/2, end);
        // ๋‘ ๋ฒˆ์งธ ์„œ๋ธŒํƒœ์Šคํฌ๋ฅผ ๋™๊ธฐ ์‹คํ–‰(์ด๋•Œ ์ถ”๊ฐ€๋กœ ๋ถ„ํ• ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค.)
        Long rightResult = rightTask.compute();
        // ์ฒซ ๋ฒˆ์งธ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์•„์ง ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ๋‹ค๋ฆฐ๋‹ค. 
        Long leftResult = leftTask.join();
        // ๋‘ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ์กฐํ•ฉํ•œ ๊ฐ’์ด ์ด ํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋‹ค.
        return leftResult + rightResult;
    }

    // ๋” ๋ถ„ํ• ํ•  ์ˆ˜ ์—†์„ ๋•Œ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋‹จ์ˆœํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜
    private long computeSequentially() {
        long sum = 0;
        for (int i = start; i < end; i++) {
            sum += numbers[i];
        }
        return sum;
    }
}

์œ„ ๋ฉ”์„œ๋“œ๋Š” n๊นŒ์ง€์˜ ์ž์—ฐ์ˆ˜ ๋ง์…ˆ ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋” ์ง๊ด€์ ์œผ๋กœ ๋ณด์—ฌ์ค€๋‹ค. ForkJoinSumCalculator์˜ ์ƒ์„ฑ์ž๋กœ ์›ํ•˜๋Š” ์ˆ˜์˜ ๋ฐฐ์—ด์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

1
2
3
4
5
public static long forkJoinSum(long n) {
    long[] numbers = LongStream.rangeClosed(1, n).toArray();
    ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
    return new ForkJoinPool().invoke(task);
}

ForkJoinPool์˜ invoke ๋ฉ”์„œ๋“œ๋กœ ์ƒ์„ฑํ•œ ํƒœ์Šคํฌ๋ฅผ ์ „๋‹ฌํ–ˆ๋‹ค. ForkJoinPool์—์„œ ์‹ซํ–‰๋˜๋Š” ๋งˆ์ง€๋ง‰ invoke ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์€ ForkJoinSumCalculator์—์„œ ์ •์˜ํ•œ ํƒœ์Šคํฌ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ForkJoinPool๋ฅผ ํ•„์š”ํ•œ ๊ณณ์—์„œ ์–ธ์ œ๋“  ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ ๋ฒˆ๋งŒ ์ธ์Šคํ„ด์Šคํ™” ํ•ด์„œ ์ •์  ํ•„๋“œ์— ์‹ฑ๊ธ€ํ„ด์œผ๋กœ ์ €์žฅํ•œ๋‹ค. (ForkJoinPool์„ ๋งŒ๋“ค๋ฉด์„œ ์ธ์ˆ˜๊ฐ€ ์—†๋Š” ๋””ํดํŠธ ์ƒ์„ฑ์ž ์ด์šฉ)

ForkJoinSumCalculator ์‹คํ–‰

  1. ForkJoinSumCalculator๋ฅผ ForkJoinPool๋กœ ์ „๋‹ฌ
  2. ํ’€์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ForkJoinSumCalculator์˜ compute ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด์„œ ์ž‘์—… ์ˆ˜ํ–‰
  3. compute ๋ฉ”์„œ๋“œ๋Š” ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์„๋งŒํผ ํƒœ์Šคํฌ์˜ ํฌ๊ธฐ๊ฐ€ ์ถฉ๋ถ„ํžˆ ์ž‘์•„์กŒ๋Š”์ง€ ํ™•์ธ
    1. ์•„์ง ํƒœ์Šคํฌ์˜ ํฌ๊ธฐ๊ฐ€ ํฌ๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด ์ˆซ์ž ๋ฐฐ์—ด์„ ๋ฐ˜์œผ๋กœ ๋ถ„ํ• ํ•ด์„œ ๋‘ ๊ฐœ์˜ ์ƒˆ๋กœ์šด ForkJoinSumCalculator๋กœ ํ• ๋‹น
    2. ๋‹ค์‹œ ForkJoinPool์ด ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ForkJoinSumCalculator๋ฅผ ์‹คํ–‰

์œ„ ๊ณผ์ •์„ ์žฌ๊ท€์ ์œผ๋กœ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ํƒœ์Šคํฌ ๋ถ„ํ• ์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

์ด์ œ ๊ฐ ์„œ๋ธŒํƒœ์Šคํฌ๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ ํฌํ‚น ํ”„๋กœ์„ธ์Šค๋กœ ๋งŒ๋“ค์–ด์ง„ ์ด์ง„ํŠธ๋ฆฌ์˜ ํƒœ์Šคํฌ๋ฅผ ๋ฃจํŠธ์—์„œ ์—ญ์ˆœ์œผ๋กœ ๋ฐฉ๋ฌธํ•œ๋‹ค.

์ฆ‰, ๊ฐ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ๋ถ€๋ถ„ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์ณ ํƒœ์Šคํฌ์˜ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.

ํ•˜๋‹ˆ์Šค๋กœ ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ํ•ฉ๊ณ„ ๋ฉ”์„œ๋“œ ์„ฑ๋Šฅ ํ™•์ธ

1
2
System.out.println("ForkJoin sum done in: " + measureSumPerf(
    ForkJoinSumCalcouator::forkJoinSum, 10_000_000) + " msecs");

๋‹ค์Œ์€ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋‹ค

1
ForkJoin sum done in: 41 msecs

๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•  ๋•Œ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ๋‚˜๋น ์กŒ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ForkJoinSumCalculator ํƒœ์Šคํฌ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ „์ฒด ์ŠคํŠธ๋ฆผ์„ long[]์œผ๋กœ ๋ณ€ํ™˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

7.2.2 ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ๋‘ ์„œ๋ธŒํƒœ์Šคํฌ๊ฐ€ ๋ชจ๋‘ ์‹œ์ž‘๋œ ๋‹ค์Œ์— join์„ ํ˜ธ์ถœํ•œ๋‹ค

    โ†’ join ๋งค์„œ๋“œ๋ฅผ ํƒœ์Šคํฌ์— ํ˜ธ์ถœํ•˜๋ฉด ํƒœ์Šคํฌ๊ฐ€ ์ƒ์‚ฐํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ํ˜ธ์ถœ์ž๋ฅผ ๋ธ”๋ก์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ

    ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ฐ๊ฐ์˜ ์„œ๋ธŒํƒœ์Šคํฌ๊ฐ€ ๋‹ค๋ฅธ ํƒœ์Šคํฌ๊ฐ€ ๋๋‚˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ผ์ด ๋ฐœ์ƒํ•˜๋ฉฐ ์›๋ž˜ ์ˆœ์ฐจ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋ณด๋‹ค ๋Š๋ฆฌ๊ณ  ๋ณต์žกํ•œ ํ”„๋กœ๊ทธ๋žจ์ด ๋˜์–ด๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค.

  • RecursiveTask ๋‚ด์—์„œ๋Š” ForkJoinPool์˜ invoke ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค.

    โ†’ ์ˆœ์ฐจ ์ฝ”๋“œ์—์„œ ๋ณ‘๋ ฌ ๊ณ„์‚ฐ์„ ์‹œ์ž‘ํ•  ๋•Œ๋งŒ invoke๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • ์„œ๋ธŒํƒœ์Šคํฌ์— fork ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ForkJoinPool์˜ ์ผ์ •์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.

    ์ด๋•Œ ์™ผ์ชฝ ์ž‘์—… ์˜ค๋ฅธ์ชฝ ์ž‘์—… ๋ชจ๋‘์— fork ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ•œ์ชฝ ์ž‘์—…์—๋Š” compute๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ด๋‹ค.

    ๊ทธ๋Ÿฌ๋ฉด ๋‘ ์„œ๋ธŒ ํƒœ์Šคํฌ์˜ ํ•œ ํƒœ์Šคํฌ์—์„œ ๊ฐ™์€ ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ถˆํ•„์š”ํ•œ ํƒœ์Šคํฌ๋ฅผ ํ• ๋‹นํ•˜๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ณ‘๋ ฌ ๊ณ„์‚ฐ์€ ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

    ๋ณดํ†ต IDE๋กœ ๋””๋ฒ„๊น…ํ•  ๋•Œ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋กœ ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚œ ๊ณผ์ •์„ ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ fork๋ผ ๋ถˆ๋ฆฌ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ compute๋ฅผ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๊ฐ€ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.

7.2.3 ์ž‘์—… ํ›”์น˜๊ธฐ

๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๊ธฐ์— ์ฝ”์–ด๊ฐ€ 4๊ฐœ ๋ฟ์ด๋ฏ€๋กœ ์ฒœ ๊ฐœ ์ด์ƒ์˜ ์„œ๋ธŒํƒœ์Šคํฌ๋Š” ์ž์›๋งŒ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ฝ”์–ด ๊ฐœ์ˆ˜์™€ ๊ด€๊ณ„์—†์ด ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ๋ถ„ํ• ๋œ ๋งŽ์€ ํƒœ์Šคํฌ๋ฅผ ํฌํ‚นํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค. ์ด๋ก ์ ์œผ๋กœ๋Š” ์ฝ”์–ด ๊ฐœ์ˆ˜๋งŒํผ ๋ณ‘๋ ฌํ™”๋œ ํƒœ์Šคํฌ๋กœ ์ž‘์—…๋ถ€ํ•˜๋ฅผ ๋ถ„ํ• ํ•˜๋ฉด ๋ชจ๋“  CPU ์ฝ”์–ด์—์„œ ํƒœ์Šคํฌ๋ฅผ ์‹คํ–‰ํ•  ๊ฒƒ์ด๊ณ  ํฌ๊ธฐ๊ฐ€ ๊ฐ™์€ ๊ฐ๊ฐ์˜ ํƒœ์Šคํฌ๋Š” ๊ฐ™์€ ์‹œ๊ฐ„์— ์ข…๋ฃŒ๋  ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ˜„์‹ค์€ ๊ฐ๊ฐ์˜ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ์ž‘์—…์™„๋ฃŒ ์‹œ๊ฐ„์ด ํฌ๊ฒŒ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋ถ„ํ•  ๊ธฐ๋ฒ•์ด ํšจ์œจ์ ์ด์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ผ ์ˆ˜๋„ ์žˆ๊ณ  ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ๋””์Šคํฌ ์ ‘๊ทผ ์†๋„๊ฐ€ ์ €ํ•˜๋˜์—ˆ๊ฑฐ๋‚˜ ์™ธ๋ถ€ ์„œ๋น„์Šค์™€ ํ˜‘๋ ฅํ•˜๋Š” ๊ณผ์ •์—์„œ ์ง€์—ฐ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ž‘์—… ํ›”์น˜๊ธฐ

ForkJoinPool์˜ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ๊ฑฐ์˜ ๊ณต์ •ํ•˜๊ฒŒ ๋ถ„ํ• ํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ์—๊ฒŒ ํ• ๋‹น๋œ ํƒœ์Šคํฌ๋ฅผ ํฌํ•จํ•˜๋Š” ์ด์ค‘ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฐธ์กฐํ•˜๋ฉด์„œ ์ž‘์—…์ด ๋๋‚  ๋•Œ๋งˆ๋‹ค ํ์˜ ํ—ค๋“œ์—์„œ ๋‹ค๋ฅธ ํƒœ์Šคํฌ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

ํ•œ ์Šค๋ ˆ๋“œ์˜ ํ• ์ผ์ด ๋‹ค ๋– ๋Ÿฌ์ง„ ์ƒํƒœ์—์„œ ํ•ด๋‹น ์Šค๋ ˆ๋“œ์˜ ์ž‘์—…์ด ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ ํ์˜ ๊ผฌ๋ฆฌ์—์„œ ์ž‘์—…์„ ํ›”์ณ์˜จ๋‹ค. ๋ชจ๋“  ํƒœ์Šคํฌ๊ฐ€ ์ž‘์—…์„ ๋๋‚  ๋•Œ๊นŒ์ง€, ์ฆ‰ ๋ชจ๋“  ํ๊ฐ€ ๋นŒ ๋•Œ๊นŒ์ง€ ์ด ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ํƒœ์Šคํฌ์˜ ํฌ๊ธฐ๋ฅผ ์ž‘๊ฒŒ ๋‚˜๋ˆ„์–ด์•ผ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ ๊ฐ„์˜ ์ž‘์—…๋ถ€ํ•˜๋ฅผ ๋น„์Šทํ•œ ์ˆ˜์ค€์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

7.3 Spliterator ์ธํ„ฐํŽ˜์ด์Šค

์ž๋ฐ” 8์€ Spliterator๋ผ๋Š” ์ƒˆ๋กœ์šด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ปฌ๋ ‰์…˜ ํ”„๋ ˆ์ž„์›Œํฌ์— ํฌํ•จ๋œ ๋ชจ๋“  ์ž๋ฃŒ๊ตฌ์กฐ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋””ํดํŠธ Spliterator ๊ตฌํ˜„์„ ์ œ๊ณตํ•œ๋‹ค.

Spliterator: ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ˜๋ณต์ž

Iterator์ฒ˜๋Ÿผ Spliterator๋Š” ์†Œ์Šค์˜ ์š”์†Œ ํƒ์ƒ‰ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค๋Š” ์ ์€ ๊ฐ™์ง€๋งŒ Spliterator๋Š” ๋ณ‘๋ ฌ ์ž‘์—…์— ํŠนํ™”๋˜์–ด ์žˆ๋‹ค.

  • Spliterator ์ธํ„ฐํŽ˜์ด์Šค
1
2
3
4
5
6
public interface Spliterator<T> {
    boolean tryAdvance(Consumer<? super T> action);
    Spliterator<T> trySplit();
    long estimateSize();
    int characteristics();
}

์—ฌ๊ธฐ์„œ T๋Š” Spliterator์—์„œ ํƒ์ƒ‰ํ•˜๋Š” ์š”์†Œ์˜ ํ˜•์‹์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

  • tryAdvance

    Spliterator์˜ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์”ฉ ์ˆœ์ฐจ์ ์œผ๋กœ ์†Œ๋น„ํ•˜๋ฉด์„œ ํƒ์ƒ‰ํ•ด์•ผ ํ•  ์š”์†Œ๊ฐ€ ๋‚จ์•„์žˆ์œผ๋ฉด ์ฐธ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (์ฆ‰, ์ผ๋ฐ˜์ ์ธ Iterator ๋™์ž‘๊ณผ ๊ฐ™๋‹ค)

  • trySplit

    Spliterator์˜ ์ผ๋ถ€ ์š”์†Œ(์ž์‹ ์ด ๋ฐ˜ํ™˜ํ•œ ์š”์†Œ)๋ฅผ ๋ถ„ํ• ํ•ด์„œ ๋‘ ๋ฒˆ์งธ Spliterator๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค.

  • estimateSize

    Spliterator์—์„œ ํƒ์ƒ‰ํ•ด์•ผ ํ•  ์š”์†Œ ์ˆ˜ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

ํƒ์ƒ‰ํ•ด์•ผํ•  ์š”์†Œ ์ˆ˜๊ฐ€ ์ •ํ™•ํ•˜์ง„ ์•Š๋”๋ผ๋„ ์ œ๊ณต๋œ ๊ฐ’์„ ์ด์šฉํ•ด์„œ ๋” ์‰ฝ๊ณ  ๊ณตํ‰ํ•˜๊ฒŒ Spliterator๋ฅผ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋‹ค.

7.3.1 ๋ถ„ํ•  ๊ณผ์ •

์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์ŠคํŠธ๋ฆผ ๋ถ„ํ•  ๊ณผ์ •์€ ์žฌ๊ท€์ ์œผ๋กœ ์ผ์–ด๋‚œ๋‹ค.

  • 1๋‹จ๊ณ„ - ์ฒซ ๋ฒˆ์งธ Spliterator์—์„œ trySplit์„ ํ˜ธ์ถœํ•˜๋ฉด ๋‘ ๋ฒˆ์งธ Spliterator๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
  • 2๋‹จ๊ณ„ - ๋‘ ๊ฐœ์˜ Spliterator์— trySplit์„ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋ฉด ๋„ค ๊ฐœ์˜ Spliterator๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

์ด์ฒ˜๋Ÿผ trySplit์˜ ๊ฒฐ๊ณผ๊ฐ€ null(๋” ์ด์ƒ ์ž๋ฃŒ๊ตฌ์กฐ ๋ถ„ํ•  ๋ชปํ•จ)์ด ๋  ๋•Œ๊นŒ์ง€ ์ด ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

  • 4๋‹จ๊ณ„ - ๋ชจ๋“  trySplit์˜ ๊ฒฐ๊ณผ๊ฐ€ null์ด๋ฉด ์žฌ๊ท€ ๋ถ„ํ•  ๊ณผ์ • ์ข…๋ฃŒ

์ด ๋ถ„ํ•  ๊ณผ์ •์€ characteristics ๋ฉ”์„œ๋“œ๋กœ ์ •์˜ํ•˜๋Š” Spliterator์˜ ํŠน์„ฑ์— ์˜ํ–ฅ์„ ๋ฐ›๋Š”๋‹ค.

Spliterator ํŠน์„ฑ

  • characteristics
    • Spliterator ์ž์ฒด์˜ ํŠน์„ฑ ์ง‘ํ•ฉ์„ ํฌํ•จํ•˜๋Š” int ๋ฐ˜ํ™˜
    • ์ด ํŠน์„ฑ์„ ์ฐธ๊ณ ํ•ด์„œ Spliterator๋ฅผ ๋” ์ž˜ ์ œ์–ดํ•˜๊ณ  ์ตœ์ ํ™” ๊ฐ€๋Šฅ

7.3.2 ์ปค์Šคํ…€ Spliterator ๊ตฌํ˜„ํ•˜๊ธฐ

  • ๋ฐ˜๋ณตํ˜•์œผ๋กœ ๋‹จ์–ด ์ˆ˜๋ฅผ ์„ธ๋Š” ๋ฉ”์„œ๋“œ
1
2
3
4
5
6
7
8
9
10
11
12
13
public int countWordsIteratively(String s) {
    int counter = 0;
    boolean lastSpace = true;
    for (char c : s.toCharArray()) { // ๋ฌธ์ž์—ด์˜ ๋ชจ๋“  ๋ฌธ์ž๋ฅผ ํ•˜๋‚˜์”ฉ ํƒ์ƒ‰ํ•œ๋‹ค.
        if (Character.isWhitespace(c)) {
            lastSpace = true;
        } else {
            if (lastSpace) counter++;
            lastSpace = false;
        }
    }
    return counter;
}

ํ•จ์ˆ˜ํ˜•์œผ๋กœ ๋‹จ์–ด ์ˆ˜๋ฅผ ์„ธ๋Š” ๋ฉ”์„œ๋“œ ์žฌ๊ตฌํ˜„ํ•˜๊ธฐ

์šฐ์„  String์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ์€ int, long, double ๊ธฐ๋ณธํ˜•๋งŒ ์ œ๊ณตํ•˜๋ฏ€๋กœ Stream๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

1
2
Stream<Character> stream = IntStream.range(0, SENTENCE.length())
                                    .mapToObj(SENTENCE::charAt);

์ŠคํŠธ๋ฆผ์— ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๋ฉด์„œ ๋‹จ์–ด ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์„ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ๋‹จ์–ด ์ˆ˜๋ฅผ ์„ธ๋Š” ํด๋ž˜์Šค
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class WordCounter {
    private final int counter;
    private final boolean lastSpace;

    public WordCounter(int counter, boolean lastSpace) {
        this.counter = counter;
        this.lastSpace = lastSpace;
    }

    public WordCounter accumulate(Character c) {
        if (Character.isWhitespace(c)) {
            return lastSpace ? this : new WordCounter(counter, true);
        } else {
            return lastSpace ? new WordCounter(counter+1, false) : this;
        }
    }

    public WordCounter combine(WordCounter wordCounter) {
        return new WordCounter(counter + wordCounter.counter, wordCounter.lastSpace);
    }

    public int getCounter() {
        return counter;
    }
}
  • accumulate ๋ฉ”์„œ๋“œ
    • WordCounter์˜ ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ฐ”๊ฟ€ ๊ฒƒ์ธ์ง€, ๋˜๋Š” ์—„๋ฐ€ํžˆ WordCounter๋Š” ๋ถˆ๋ณ€ ํด๋ž˜์Šค์ด๋ฏ€๋กœ ์ƒˆ๋กœ์šด WordCounter ํด๋ž˜์Šค๋ฅผ ์–ด๋–ค ์ƒํƒœ๋กœ ์ƒ์„ฑํ•  ๊ฒƒ์ธ์ง€ ์ •์˜
    • ์ŠคํŠธ๋ฆผ์„ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ๋ฌธ์ž๋ฅผ ์ฐพ์„ ๋•Œ๋งˆ๋‹ค accumulate ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ
    • ์œ„์˜ ๋ฐ˜๋ณตํ˜• ๋ฉ”์„œ๋“œ countWordsIteratively์—์„œ์ฒ˜๋Ÿผ ์ƒˆ๋กœ์šด ๋น„๊ณต๋ฐฑ ๋ฌธ์ž๋ฅผ ํƒ์ƒ‰ํ•œ ๋‹ค์Œ ๋งˆ์ง€๋ง‰ ๋ฌธ์ž๊ฐ€ ๊ณต๋ฐฑ์ด๋ฉด counter๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

  • combine ๋ฉ”์„œ๋“œ
    • ๋ฌธ์ž์—ด ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ WordCounter์˜ ๊ฒฐ๊ณผ ํ•ฉ์นจ
  • ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ ๊ตฌํ˜„
1
2
3
4
5
6
private int countWords(Stream<Character> stream) {
    WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
                                                WordCounter::accumulate,
                                                WordCounter::combine);
    return wordCounter.getCounter();
}
  • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
1
2
3
Stream<Character> stream = IntStream.range(0, SENTENCE.length())
                                    .mapToObj(SENTENCE::charAt);
System.out.println("Found " + countWords(stream) + " words");

WordCounter ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ

1
System.out.println("Found " + countWords(stream.parallel()) + " words");

๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ ์ž ํ–ˆ์ง€๋งŒ ๊ฒฐ๊ณผ๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ๋‚˜์˜จ๋‹ค.

์›๋ž˜ ๋ฌธ์ž์—ด์„ ์ž„์˜์˜ ์œ„์น˜์—์„œ ๋‘˜๋กœ ๋‚˜๋ˆ„๋‹ค๋ณด๋‹ˆ ์˜ˆ์ƒ์น˜ ๋ชปํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ๋‹จ์–ด๋ฅผ ๋‘˜๋กœ ๊ณ„์‚ฐํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊ฟ€ ๋•Œ ์ŠคํŠธ๋ฆผ ๋ถ„ํ•  ์œ„์น˜์— ๋”ฐ๋ผ ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

โ†’ ๋ฌธ์ž์—ด์„ ์ž„์˜์˜ ์œ„์น˜์—์„œ ๋ถ„ํ• ํ•˜์ง€ ๋ง๊ณ  ๋‹จ์–ด๊ฐ€ ๋๋‚˜๋Š” ์œ„์น˜์—์„œ ๋ถ„ํ• ํ•˜๋ฉด ๋œ๋‹ค. ๋‹จ์–ด ๋์—์„œ ๋ฌธ์ž์—ด์„ ๋ถ„ํ• ํ•˜๋Š” ๋ฌธ์ž Spliterator๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class WordCounterSpliterator implements Spliterator<Character> {
    private final String string;
    private int currentChar = 0;

    public WordCounterSpliterator(String string) {
        this.string = string;
    }

    @Override
    public boolean tryAdvance(Consumer<? super Character> action) {
        action.accept(string.charAt(currentChar++)); // ํ˜„์žฌ ๋ฌธ์ž๋ฅผ ์†Œ๋น„ํ•œ๋‹ค.
        return currentChar < string.length(); // ์†Œ๋น„ํ•  ๋ฌธ์ž๊ฐ€ ๋‚จ์•„์žˆ์œผ๋ฉด true ๋ฐ˜ํ™˜
    }

    @Override
    public Spliterator<Character> trySplit() {
        int currentSize = string.length() - currentChar;
        if (currentSize < 10) {
            return null;
        }
        for (int splitPos = currentSize / 2 + currentChar;
                splitPos < string.length(); splitPos++) {
            // ๋‹ค์Œ ๊ณต๋ฐฑ์ด ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๋ถ„ํ•  ์œ„์น˜๋ฅผ ๋’ค๋กœ ์ด๋™ ์‹œํ‚ด
            if (Character.isWhitespace(string.charAt(splitPos))) {
                Spliterator<Character> spliterator = 
                    new WordCounterSpliterator(string.substring(currentChar, 
                    splitPos));
                currentChar = splitPos;
                return spliterator;
            }
        }
        return null;
    }

    @Override
    public long estimateSize() {
        return string.length() - currentChar;
    }

    @Override
    public int characteristics() {
        return ORDERED + SIZED + SUBSIZED + NON-NULL + IMMUTABLE;
    }
}
This post is licensed under CC BY 4.0 by the author.