본문으로 바로가기

멀티쓰레드를 사용할때는 가장 중요한게 CPU 사용율을 높이더라고 소요시간이 낮은걸 택하면서 코딩을 해야한다. 

멀티쓰레드의 핵심은 여러개를 실행시켜 속도를 향상시키는것이다(병렬).

만약 멀티쓰레드를 사용했음에도 불구하고 직렬처리와 같다면 옳지 못한 방법이다. 직렬방식은 지양해야 한다. 

보통 잘 만든 프로그램은 혼자 작동할때 CPU 성능을 70~80% 유지한다.

프로그램은 Runnable과 Callable 둘다 만들수 있다. 하지만 무엇을 선택해서 만들지는 잘 생각해보고 만들어야한다. 

Runnable은 리턴값이 존재하지 않기 때문에 주로 공유객체를 사용할때 사용한다. 편리하지만 공유객체를 만들어야한다는 단점이 있다. Callable은 리턴값이 존재하기 때문에 공유 객체를 사용하지 않는다는 장점이 있지만 Runnable에 비해 복잡해질수 있다.


중복되지 않는 숫자 100개를 멀티 쓰레드로 만들어 내는 프로그램을 만들어보자.





Runnable 예제)


<RandomNumberAdder.java>


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
 
import java.util.List;
import java.util.Random;
 
public class RandomNumberAdder implements Runnable {
 
    private List<Integer> genreated;
 
    public RandomNumberAdder(List<Integer> generated) {
        this.genreated = generated;
    }
 
    @Override
    public void run() {
        try {
            Thread.sleep(10L);
        } catch (InterruptedException e) {        
            e.printStackTrace();
        }
 
        while (true) {
            int randomNumber = new Random().nextInt(100);        
            synchronized (genreated) {
                if (!genreated.contains(randomNumber)) {
                    genreated.add(randomNumber);
                    return;
                }
            }
        }
 
    }
 
}
 
cs


<UniqueNumberExecutor.java>


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
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
 
public class UniqueNumberExecutor {
    private ExecutorService executor;
    private List<Integer> generated = new ArrayList<>();
 
    public void init() {
        executor = Executors.newFixedThreadPool(100);
    }
 
    public void shutdown() {
        executor.shutdown();
        try {
            executor.awaitTermination(5, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public void execute(List<RandomNumberAdder> generators) {
        generators.stream().forEach(generator -> executor.execute(generator));
    }
 
    public static void main(String[] args) {
                
        UniqueNumberExecutor uniquenumberExecutor = new UniqueNumberExecutor();
        final List<RandomNumberAdder> generator = new ArrayList<>();
        IntStream.range(0100).forEach(i -> generator.add(new RandomNumberAdder(uniquenumberExecutor.generated)));
         
        uniquenumberExecutor.init();
        long startTime = System.nanoTime(); 
        uniquenumberExecutor.execute(generator);
        uniquenumberExecutor.shutdown();
        System.out.println((System.nanoTime() - startTime) / 1000_000 + "ms");
 
        Collections.sort(uniquenumberExecutor.generated);
 
        uniquenumberExecutor.generated.stream().forEach(System.out::println);
        
    }
}
 
cs





Callable 예제)


<Callable.java>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Random;
import java.util.concurrent.Callable;
 
public class RandomNumberAdder implements Callable<Integer> {
 
    @Override
    public Integer call() throws Exception {
        Thread.sleep(10);
        return new Random().nextInt(100);
    }
 
}
 
cs



<UniqueNumberExecutor.java>

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
54
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
 
public class UniqueNumberExecutor {
 
    public static void main(String[] args) {
        Set<Integer> list = new TreeSet<>();
 
        long startTime = System.nanoTime();
 
        while (list.size() < 100) {
            List<Future<Integer>> futures = new ArrayList<>();
            ExecutorService executor = Executors.newFixedThreadPool(100);
            futures.add(executor.submit(new RandomNumberAdder()));
 
            for (int i = 0; i < 100; i++) {
                futures.add(executor.submit(new RandomNumberAdder()));
            }
 
            executor.shutdown();
            try {
                executor.awaitTermination(5, TimeUnit.MINUTES);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
 
            for (Future<Integer> future : futures) {
                if (list.size() >= 100) {
                    break;
                }
 
                try {
                    list.add(future.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
 
 
        System.out.println((System.nanoTime() - startTime) / 1000_000 + "ms");
        list.stream().forEach(System.out::println);
    }
}
 
cs

Callable의 핵심은 여러개를 불러서 계속 시키는것이다. 답이 나올때 까지!!

'프로그래밍 > 자바' 카테고리의 다른 글

[자바] Thread Safety란?  (0) 2019.01.30
[자바] 서버 프로그래밍 조직화  (0) 2019.01.30
람다식이란?  (0) 2019.01.28
[JAVA] 쓰레드풀(ThreadPoolExecutor) 정리  (0) 2019.01.27
[JAVA] 데몬(daemon) 스레드 정리  (0) 2019.01.27