[Java] ๋ณ‘๋ ฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ "๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ"

    "๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ" ์€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ๋ณ‘๋ ฌํ™”ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

    ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์€ ์ŠคํŠธ๋ฆผ API์˜ ํ•œ ์ข…๋ฅ˜๋กœ,๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ž‘์—…์„ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. 

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

     

    ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์€ ๋‚ด๋ถ€์ ์œผ๋กœ Fork/Join ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘๋™ํ•œ๋‹ค. 

    Fork/Join ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์ž‘์—…์„ ์ž‘์€ ์ž‘์—…๋“ค๋กœ ๋ถ„ํ• ํ•˜๊ณ , ์ด๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•œ ํ›„ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. 

    ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์€ ์ž‘์—…์„ ๋” ์ž‘์€ ์ž‘์—… ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์–ด ๋ฉ€ํ‹ฐ์ฝ”์–ด ํ”„๋กœ์„ธ์„œ์—์„œ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

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

    ๊ธฐ์กด์˜ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด parallel() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค. 

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

    ๋˜ํ•œ ๋ฐ˜๋Œ€๋กœ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚œ ํ›„์— sequential() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

    List<String> myList = Arrays.asList("apple", "banana", "orange", "grape", "strawberry");
    
    //๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์š”์†Œ๋ฅผ ๋Œ€๋ฌธ์ž๋กœ ์ถœ๋ ฅํ•œ๋‹ค.
    myList.parallelStream().map(String::toUpperCase).forEach(System.out::println);

     

    ParallelStream

    Java 8์—์„œ ๋“ฑ์žฅํ•œ Stream ์€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. 

    ์ด์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์ด parallel(), parallelStream() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•จ์„ ํ†ตํ•ด ์•Œ์•„์„œ ForkJoinFramework ๊ด€๋ฆฌ ๋ฐฉ์‹์„ ์ด์šฉํ•ด ์ž‘์—…์„ ๋ถ„ํ• ํ•˜๊ณ  ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค.

     

    Fork / Join Framework 

    ๊ทธ๋ ‡๋‹ค๋ฉด Fork / Join Framework๋Š” ๋ฌด์—‡์ผ๊นŒ?
    ์ด๋Š” ๋‹ค์–‘ํ•œ ์ž‘์—…๋“ค์„ ๋ถ„ํ•  ๊ฐ€๋Šฅํ•œ ๋งŒํผ ์ชผ๊ฐœ๊ณ , ์ด ์ชผ๊ฐœ์ง„ ์ž‘์—…๋“ค์„ Work Thread๋ฅผ ํ†ตํ•ด ์ž‘์—… ํ›„ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” ๊ณผ์ •์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค. 

    ์ฆ‰, ๋ถ„ํ•  ์ •๋ณต (Divide and Conquer) ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๋น„์Šทํ•œ ์ž‘์—…์ด๋ฉฐ, Fork ๋ฅผ ํ†ตํ•ด Task๋ฅผ ๋ถ„๋‹ดํ•˜๊ณ  Join์„ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” ๊ณผ์ •์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    Fork / Join Framework์˜ Work Thread์˜ ์ˆ˜๋Š” ํ•ด๋‹น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ตฌ๋™๋˜๋Š” ์„œ๋ฒ„์˜ ์ŠคํŽ™์— ๋”ฐ๋ผ ๋ฐ”๋€๋‹ค.

    Runtime.getRuntime().availableProcessors()์œผ๋กœ JVM์—์„œ ์ด์šฉ ๊ฐ€๋Šฅํ•œ CPU Core ๊ฐœ์ˆ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, 

    ์Šค๋ ˆ๋“œ๊ฐ€ N๊ฐœ ์ƒ์„ฑ๋˜์—ˆ์„ ๋•Œ, ํ•˜๋‚˜๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ์™€ ๋‚˜๋จธ์ง€ N-1๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ForkJoinPool ์Šค๋ ˆ๋“œ๋ผ๊ณ  ํ•œ๋‹ค.


    ๊ธฐ๋ณธ ์ŠคํŠธ๋ฆผ vs ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์˜ˆ์ œ

    ์ŠคํŠธ๋ฆผ ์˜ˆ์ œ)

    public static void main(String[] args) {
    	
        List<Integer>numbers = Arrays.asList(1,2,3,4); //Integerํƒ€์ž…์˜ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
        
        long start = System.currentTimeMillis(); //ํ˜„์žฌ ์‹œ๊ฐ„ ๊ธฐ๋ก
        
        //numbers ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
        numbers.forEach(number -> {
        	try {
            	Thread.sleep(3000); //ํ˜„์žฌ ์Šค๋ ˆ๋“œ 3์ดˆ ์ผ์‹œ ์ค‘์ง€
                System.out.println(number + ": " + Thread.currentThread().getName());
            } catch (InterruptedException e) {}
        });
        
        //์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„์˜ ์‹œ๊ฐ„ ๊ณ„์‚ฐ
        long duration = (System.currentTimeMillis() - start);
        //๋ฐ€๋ฆฌ์ดˆ๋Š” ์ดˆ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
        double seconds = duration / 1000.0;
        
        System.out.printf("Done in %.2f sec\n", seconds);
    }

     

    //์ฝ”๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ
    1: main
    2: main
    3: main
    4: main
    Done in 12.05 sec

     

    ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์˜ˆ์ œ)

    public static void main(String[] args) {   
    
    		List<Integer> numbers = Arrays.asList(1, 2, 3, 4);     
    		
            long start = System.currentTimeMillis();    
    		numbers.parallelStream().forEach(number -> {        
    			try {            
                	Thread.sleep(3000);            
    				System.out.println(number + ": " + Thread.currentThread().getName());        
    			} catch (InterruptedException e) {}    
           	});    
    		long duration = (System.currentTimeMillis() - start);    
    		double seconds = duration / 1000.0;     
    		
            System.out.printf("Done in %.2f sec\n", seconds);
    }
    //์ฝ”๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ
    3: main
    1: ForkJoinPool.commonPool-worker-2
    2: ForkJoinPool.commonPool-worker-1
    4: ForkJoinPool.commonPool-worker-3
    Done in 3.04 sec

    ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์  

    1. ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์—…์ด ์ถฉ๋ถ„ํžˆ ํฌ๊ณ , ์—ฐ์‚ฐ๋Ÿ‰์ด ๋งŽ์„ ๋•Œ์—๋งŒ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์ด ์ข‹๋‹ค. 

    2. ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์— ์•ˆ์ „ํ•ด์•ผ ํ•œ๋‹ค. 

         ex. ์ŠคํŠธ๋ฆผ์˜ ์—ฐ์‚ฐ์ด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ(๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒฝ์šฐ) ๋“ฑ์— ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. 

    3. ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. 

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

     

     

     

     

     

    ๋Œ“๊ธ€