728x90
프린트 예제 V1


- volatile : 여러 스레드가 동시에 접근하는 변수에는 "volatile" 키워드를 붙어주어야 안전하다. 여기서는 "main" 스레드, printer 스레드 둘 다 "work" 변수에 동시에 접근할 수 있다.
- ConcurrentLinkedQueue : 여러 스레드가 동시에 접근하는 경우, 컬렉션 프레임워크가 제공하는 일반적인 자료구조를 사용하면 안전하지 않다. 여러 스레드가 동시에 접근하는 경우 동시성을 지원하는 동시성 컬렉션을 사용해야 한다. Queue의 경우 ConcurrentLinkedQueue를 사용하면 된다.
프린트 동작 과정

- main 스레드: 사용자의 입력을 받아서 Printer 인스턴스의 jobQueue에 담는다.
- printer 스레드: jobQueue 가 있는지 확인한다.
- jobQueue에 내용이 있으면 poll()을 이용해서 꺼낸 다음에 출력한다.
- 출력하는 데는 약 3초의 시간이 걸린다. 여기서는 sleep(3000)를 사용해서 출력 시간을 가상으로 구현했다.
- 출력을 완료하면 while문을 다시 반복한다.
- 만약 jobQueue 가 비었다면 continue를 사용해서 다시 while문을 반복한다.
- 이렇게 해서 jobQueue에 출력할 내용이 들어올 때까지 계속 확인한다.
- jobQueue에 내용이 있으면 poll()을 이용해서 꺼낸 다음에 출력한다.

- main 스레드: 사용자가 q를 입력한다. printer.work의 값을 false로 변경한다.
- "main" 스레드는 while문을 빠져나가고 "main" 스레드가 종료된다.
- printer 스레드: while문에서 work의 값이 false 인 것을 확인한다.
- printer 스레드는 while문을 빠져나가고, "프린터 종료"를 출력하고, "printer" 스레드는 종료된다.
문제점 : 앞서 살펴보았듯이 이 방식의 문제는 종료( q )를 입력했을 때 바로 반응하지 않는다는 점이다. 왜냐하면 "printer" 스레드가 반복문을 빠져나오려면 while문을 체크해야 하는데, printer 스레드가 sleep(3000)을통해대기 상태에 빠져서 작동하지 않기 때문이다. 따라서 최악의 경우q를입력하고 3초 이후에 프린터가 종료된다.
프린트 예제 V2(인터럽트 도입)


- 종료("q")를 입력하면 즉시 종료되는 것을 확인할 수 있다. 따라서 반응성이 매우 좋아진다.
- 종료시 "main" 스레드는 work 변수도 false 로 변경하고, printer 스레드에 인터럽트도 함께 호출한다.
- 이렇게 둘 다 함께 적용하면, printer 스레드가 sleep() 을 호출한 상태는 물론이고, while (work) 코드가 실 행되는 부분에서도 빠져나올 수 있어서 반응성이 더 좋아진다.
- interrupt() : sleep() 상태에서 빠져나온다.
- work=false : while문을 체크하는 곳에서 빠져나온다.
프린트 예제 V3(인터럽트 코드 개선)


- Thread.interrupted() 메서드를 사용하면 해당 스레드가 인터럽트 상태인지 아닌지 확인할 수 있다.
- 따라서 while에서 체크하던 work 변수를 제거할 수 있다.
- work 변수로 확인하는 대신에 해당 스레드의 인터럽트 상태만 확인하면 된다.
프린트 예제 V4(yield 도입)

- 이전 코드는 인터럽트가 발생하기 전까지 계속 인터럽트의 상태를 체크하고 또 jobQueue의 상태를 확인한다.
- 문제는 쉴 틈 없이 CPU에서 이 로직이 계속 반복해서 수행된다는 점이다. 1초에 while문을 수억 번 반복할 수도 있다! 결과적으로 CPU 자원을 많이 사용하게 된다.
- 현재 작동하는 스레드가 아주 많다고 가정해 보자. 인터럽트도 걸리지 않고, jobQueue 도 비어있는데, 이런 체크 로직에 CPU 자원을 많이 사용하게 되면, 정작 필요한 스레드들의 효율이 상대적으로 떨어질 수 있다.
- 차라리 그 시간에 다른 스레드들을 더 많이 실행해서 jobQueue에 필요한 작업을 빠르게 만들어 넣어주는 게 더 효율적일 것이다. 그래서 다음과 같이 jobQueue에 작업이 비어있으면 yield()를 호출해서, 다른 스레드에 작업을 양보하는 게 전체관점에서 보면 더 효율적이다.
728x90
'멀티스레드와 동시성' 카테고리의 다른 글
| Ch05. 메모리 가시성 - volatile, 메모리 가시성 (0) | 2024.08.08 |
|---|---|
| Ch04. 스레드 제어와 생명 주기2 - yield (0) | 2024.08.08 |
| Ch04. 스레드 제어와 생명 주기2 - 인터럽트 (0) | 2024.08.06 |
| Ch03. 스레드 제어와 생명 주기 - Join (0) | 2024.08.04 |
| Ch03. 스레드 제어와 생명 주기 - 체크 예외 재정의 (0) | 2024.08.02 |