운영체제는 여러개의 프로그램을 동시에 실행 할 수 있도록 발전했고,
프로세스마다 메모리, 파일 핸들 , 보안 권한등 자원을 할당한다.
- 프로세스 끼리는 소켓, 공유 메모리, 세마포어, 파일로 서로 통신이 가능하다.
리소스 활용, 공정성, 편의성 등의 이유로 동시에 프로세스를 실행시키는 운영체제를 개발 하게 된건데
스레드도 마찬가지 이유로 고안됏다.
(
- 리소스 활용: 프로그램은 가끔 인풋 이나 아웃풋 처럼 외부 작동을 기다리고 있어야 하는데,
기다리는 동안 일을 할 수 없다. 그래서 대기 시간 동안 다른 프로그램이 작동하면 효율적일 수 있다.
- 공정성
여러 사용자와 프로그램이 컴퓨터 내 자원에 대해 동일한 권한을 가질 수 있음
-편의성: 모든 tasks 를 실행하는 싱글 프로그램을 작성하는것보다
single task 를 수행하는 여러개의 프로그램을 작성하는게 더 쉽다.
)
스레드로 한 프로세스 내에 여러개의 프로그램 제어 흐름이 공존 할 수 있게 됐다.
스레드는 메모리, 파일 핸들 같은 프로세스 자원을 공유하고
각기 프로그램 카운터, 스택, 지역 변수를 별도로 갖고 있다.
스레드로 나뉘기 때문에 , 자연 스럽게 하드웨어 병렬성을 이용할 수 있다.
여러개의 CPU 에 스레드를 할당 해서 실행 할 수 있다.
스레드는 비동기적으로 실행이 되고,
스레드는 프로세스의 메모리 주소 공간을 공유하기 때문에
한 프로세스 내 스레드들은 같은 변수에 접근 하고 힙에 객체를 할당한다.
그래서 공유 데이터를 적절하게 동기화 하지 않으면 예상치 못한 결과를 얻을 수 있다.
스레드의 장점
스레드를 올바르게 사용하면 개발 및 관리 비용을 절갑하고 복잡한 프로그램의 성능을 향상시킬수있다.
스레드는 비동기 워크플로우를 순차적 워크 플로우로 전환해 쉽게 모델링 할 수 있다.
복잡한 코드를 쓰기, 읽기 및 유지 관리가 쉬운 직선 코드로 변환 할 수 있다.
- 멀티 프로세서 활용
클럭 속도를 높이는 것이 어려워져서 제조업체들은 하나의 칩에 더 많은 프로세서 코어를 넣고 있다.
이미 우리는 한 컴퓨터에 많은 프로세서 의 수가 있는 걸 볼 수 있다.
예약의 기본 단위는 스레드이고 코어가 하나만 있는 프로세서에서는 스레드는 최대 하나만 실행 시킬 수 있다.
프로세서가 2개인 곳에서 스레드가 하나 뿐인 프로그램을 실행하면 CPU 절반을 사용하지 못하는것이다.
* 대부분의 제조업체에서 멀티 프로세서를 사용 하고 있고,
스레드를 사용하게 되면, 처리 속도를 높일 수 있다. 예를 들어 스레드 하나가 I/O 작업을 대기 하고 있어도
다른 스레드가 작업을 할 수 있다.
- 모델링의 간단화
* 종류별 작업마다 스레드를 하나 씩 할당하면, 마치 순차적인 작업처럼 처리 할 수 있고,
스케줄링, 교차 실행, 비동기 I/O 등의 세부적인 부분과 비즈니스 로직을 분리 할 수 있다.
개발을 단순화 하고 학습 커브를 줄일 수 있다.
- 단순한 비동기 이벤트 처리
단일 스레드 서버 프로그램에서는 스레드 하나가 I/O 작업을 하기 위해 멈춰 있으면,
다른 모든 요청들도 처리하지 못하고 멈춰 있는다.
이걸 해결하려면 복잡하고 실수하기 쉬운 넌블로킹 I/O 기능을 써야 한다.
별개 스레드에서 처리하게 되면 대기 상태에 들어가도 다른 스레드가 요청을 처리 할 수 있다.
스레드 사용의 위험성
동기화가 충분하지 않으면 스레드의 작업 순서를 예측할 수 없다.

서로 다른 스레드가 타이밍이 좋지 않아서 getNext 를 호출했을때 동일한 값을 받을 수 있다.
변수 1을 증가 시키는건 읽고 더하고 쓰는 3개의 개별 작업으로 나눠진다.
여러 스레드에서 작업이 수행되면서 동시에 값을 추가 해서
잘못된 값을 내보낼 수 있다.
UnsafeSequence 에서는
경쟁 조건race condition 이라는 일반적인 동시성 위험을 나타내고 있다.
실행 과정에서 연산이 어떻게 서로를 간섭하냐에 따라 결과가 달라질 수 있다.
스레드는 동일 메모리 주소 공간을 공유하고 실행되기 때문에
다른 스레드가 사용할 수 있는 변수에 접근하고 수정할 수 있다.
매우 편리하고 스레드간 통신 메커니즘보다 훨씬 쉽게 데이터를 공유할 수 있다.
하지만 이게 크리티컬한 위험이기도 하다.
예기치 않게 데이터가 변경되어 스레드를 혼동 시킬 수 있다.
멀티 스레드 프로그램에서 예측이 가능하려면공유 변수에 대한 엑세스가 적절히 조정되어야 한다.
Java 는 이러한 접근을 조정하기 위해 synchronize 를 제공한다.
위 getNext는 다음과 같이 동기화 방법으로 수정할 수 있다.

동기화를 하지 않으면, 명령어의 실행 시점이나 순서가 상당히 자유롭게 조정이 돼고
캐시 메모리에 변수를 저장 할 수 도 있어 최적화가 가능해진다.
다만, 이런 최적화 작업 때문에 오류가 발생하지 않게 스레드 간에 데이터가 공유되고 있는지를 명확하게
구분해 줘야 되는 부담이 생긴다.
- 활동성 위험
활동성이 "원하는 일이 결국 일어난다" 를 뜻할때,
스레드를 사용하면 활동성의 문제가 발생할 위험성이 커진다.
스레드 A 에서 스레드B 의 리소스를 기다리는 경우
스레드B 가 자원을 방출하지 않으면 A는 영원히 기다리게 된다.
데드락 , 소모상태(starvness), 라이브락등 concurrency 버그들이 발생할 수 있다.
10장에서 활동성 장애 유형을 보고, 어떻게 각 유형의 장애를 피할 수 있는지 알아보자
- 성능 위험
잘 설계된 동시 어플리케이션에서 스레드 사용은 성능을 향상시킬 수 있지만,
스레드에는 일정 수준의 런타임 오버헤드가 발생된다.
컨텍스트 스위치 - 활성 스레드가 일시 중단되면 또 다른 스레드가 실행이 되는데
스레드가 많은 어플리케이션일수록 더 자주 실행된다
실행 컨텍스트 저장, 복원 등 스케줄링 하는 CPU 시간 등 상당한 비용이 소요된다.
'Java > 병렬 프로그래밍' 카테고리의 다른 글
자바 병렬 프로그래밍 - 작업 실행 (0) | 2021.01.07 |
---|---|
자바 병렬 프로그래밍 - 구성 단위 (0) | 2021.01.05 |
자바 병렬 프로그래밍 - 객체 구성 (0) | 2021.01.04 |
자바 병렬 프로그래밍 - 객체 공유 (0) | 2020.12.19 |
자바 병렬프로그래밍 - 스레드 세이프티 (0) | 2020.12.16 |