병렬 처리
여러 개의 작업을 동시에 실행하는 것을 말합니다.
동시에 해결할 수 있는 부분을 분산처리 함으로써 처리의 부하를 분담하고, 작업 시간을 단축시키는 방법입니다.
동기 처리와 비동기 처리
동기 처리
: 요청을 보낸 후 응답을 받아야만 다음 동작이 진행되는 처리방식 입니다.
비동기 처리
: 요청을 보낸 후 응답을 기다리지 않고 다른 작업을 실행하고 있는 처리방식 입니다.
동기화(Synchronization)
- 여러 개의 Thread가 한 개의 자원을 사용하고자 할 때,해당 Thread를 제외한 나머지는 접근을 못하도록 막는 걸 말합니다.
- 동기화를 통해
race condition
,deadlock
등의 문제를 해결할 수 있습니다.
멀티 스레드
병렬처리 방법 중 하나입니다.
하나의 프로세스
에서 다수의 스레드
를 사용해 동시에 여러 작업을 처리하는 걸 멀티 스레드(멀티 스레딩)이라고 합니다.
멀티 스레드를 동작시키는 방식 - 동시성과 병렬성
-
동시성(Concurrency)
- 싱글 코어에서 멀티스레드를 동작시키기 위한 방식으로, 멀티 태스킹을 위해 여러 개의 스레드가 '번갈아가면서' 실행되는 성질입니다.
- 동시에 실행되는 '것처럼 보이게'되는 걸 동시성이라 합니다.
- 콘텍스트 스위칭이 발생합니다.
-
병렬성(Parallelism)
- 멀티 코어에서 멀티스레드를 동작시키는 방식으로, 한 개 이상의 스레드를 포함하는 각 코어들이 '동시에' 실행되는 성질입니다.
프로세스
- 프로세스는 실행중인 프로그램을 말합니다.
- 프로세스는 stack, heap, data, code로 이루어져 있습니다.
- 프로그램이 프로세스가 되는 과정
- Data,Code,Heap,Stack이 메모리에 올라가 메모리 공간을 확보합니다.
- 해당 프로세스에 대한 정보를 담고 있는 PCB가 프로세스 생성과 함께 만들어집니다.
스레드
- 스레드는 프로세스 내에서 실제로 작업을 수행하는 주체입니다.
많은 사람들이 프로세스는 공장, 스레드는 공장에서 일하는 일꾼으로 비유하곤 합니다.
멀티 스레드의 장점
- 멀티 스레드는 자원활용의 효율성이 좋습니다.
- 메모리를 공유하기 때문에 메모리 절약성이 높습니다.
- 각각의 멀티 스레드는 하나의 Method Area와 Heap Memory를 공유하고 있기 때문에 통신의 부담이 적습니다.
- cf)프로세스간의 자원공유에는 IPC통신이 필요합니다.
멀티 스레드의 단점
- 프로그램의 복잡도가 증가합니다.
- 자원을 공유하기 때문에 한 곳에서의 잘못이 전체에 영향을 줄 수 있습니다.
멀티 프로세스와 멀티 스레드
- 멀티 프로세스는 멀티 스레드보다 context switching비용이 비쌉니다.
- 멀티 프로세스는 캐쉬 메모리 초기화 등의 무거운 작입이 진행됩니다.
- context swtiching은 기존 프로세스의 context를 저장하고, 다음 프로세스를 진행할 수 있게 context를 교체하는 작업입니다.
- 멀티 프로세스는 프로세스간 통신비용이 비쌉니다.
- 프로세스간의 자원공유에는 IPC통신이 필요합니다.
멀티 스레드 환경 주의사항
race condition(경쟁 상태)
- 여러 스레드가 공유자원에 동시에 접근하려 할 때 일어나는 문제입니다.
- 'A스레드는 변수의 값을 변경하고 있는데 B스레드가 해당 변수를 사용사려고 하는 상황'이 경쟁 상태의 한 예시입니다.
- 경쟁 상태가 존재하면 프로그래머가 설계한대로 코드가 작동하지 않을 가능성이 크기 때문에 이를 없애줘야 합니다.
해결방법 - 뮤텍스와 세마포어
임계 영역
설정을 통해 두 자원이 동시에 접근하지 못하도록 막는 걸 상호 배제
라고 합니다.
뮤텍스
와 세마포어
도 상호 배제의 하나입니다.
- 뮤텍스와 세마포어는 동기화(Synchronization)를 활용한 방법들입니다.
- 상호 배제 이외에도
진행(progress)
과 한계 대기(bounded waiting)
등의 경쟁 상태 해결방법이 있습니다.
- 진행: 임계 구역을 실행하고 있는 프로세스가 없을 때, 몇 개의 프로세스가 임계 구역에 진입하고자 하면 이들의 진입 순서는 이들에 의해서만 결정되어야 한다. 또한 이 선택은 무한정 연기되어서는 안된다.
- 한계 대기: 한 프로세스가 자신의 임계 구역에 진입하고자 요청을 한 후부터 이 요청이 허용될 때까지 다른 프로세스가 그들의 임계 구역에 진입할 수 있는 회수가 제한되어야한다.
임계 영역(Critical Section)
임계영역은 lock을 걸어 다른 스레드가 동시에 접근하지 못하도록 설정한 구간입니다.
뮤텍스(Mutex)
공유 자원에 여러 스레드
가 접근하는 것을 막는 것을 뮤텍스라고 합니다.
세마포어(Semaphore)
공유 자원에 여러 프로세스
가 접근하는 것을 막는 것을 세마포어라고 합니다.
뮤텍스와 세마포어의 차이
- 스레드의 접근을 막는 뮤텍스, 프로세스의 접근을 막는 세마포어
- 뮤텍스는 동기화 대상이 오직 1개일때만 사용가능, 세마포어는 동기화 대상이 여럿일때 사용가능
- 뮤텍스는 오직 해당 스레드에서만 lock 해제가능, 세마포어는 다른 스레드에서도 lock 해제가능
뮤텍스와 세마포어는
하나의 서버
에서 여러 개의 프로세스, 스레드가 존재할 때 활용 가능한 Lock입니다. 대용량 트래픽을 처리하기 위해 여러 서버
를 사용할 때는 뮤텍스와 세마포어를 활용할 수 없습니다.
deadlock(교착상태)
- 교착상태란 둘 이상의 프로세스(스레드)가 서로의 작업이 끝나기만을 기다리고 있어 모두가 영원히 끝나지 않는 상황을 말합니다.
다음 4가지가 모두 존재할 때 교착상태가 발생할 수도 있습니다.(필요조건)
- 상호배제 - 자원은 한 프로세스에 의해서만 점유 가능합니다.
- 점유대기 - 프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다립니다.
- 비선점 - 다른 프로세스가 점유한 자원은 강제로 빼앗을 수 없습니다.
- 순환대기 - 각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있습니다.
동시에 접근하지도 못하며(상호 배제) 강제로 뺐을수도 없는데(비선점) 먼저 양보도 안하고(점유대기) 서로가 서로에게 필요한 걸 가지고만 있는(순환대기) 상황에서 교착상태가 발생합니다.
교착상태 해결방법
-
교착 상태의 예방
- 운영체제를 설계할 때 교착상태가 발생할 가능성을 없애는 방법입니다.
- 예방방법은 자원 사용의 효율성이 떨어지고 비용이 많이 듭니다.
- 위 4가지 조건 중 하나를 배제함으로써 교착상태 예방을 실천합니다.
-
교착 상태의 회피
- 자원할당을 신중히 해 순환대기를 피하는 방법입니다.
- 대표 알고리즘
-
교착 상태의 발견
- 회피처럼 신중히 자원을 할당하는 게 아니라 일단 자원을 주고, 주기적으로 교착 상태가 발생하는지를 감시하는 방법입니다.
-
교착 상태의 회복
- 교착 상태를 발견했을 때 정상 상태로 되돌리는 방법을 말합니다.
- 방법
- 교착상태에 포함되어 있는 모든 프로세스들을 중지시킵니다.
- 교착상태에 포함되어 있는 각 프로세스의 수행을 롤백시킵니다.
- 교착상태가 없어질 때까지 교착상태에 포함되어 있는 프로세스들 하나씩 종료시킵니다.
- 교착상태가 없어질 때까지 교착상태에 포함되어 있는 자원을 하나씩 선점시킵니다.
Thread-safe
- Thread-safe란
멀티스레드 환경
에서 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없는 상태를 말합니다.
스레드 안전성 보장받는 방법
- lock을 사용합니다.
- synchronized 키워드를 통해 동기화를 진행해 줍니다.
- 동기화된 자료구조를 사용합니다.
- concurrent 패키지에 있는 자료구조를 통해 안전성을 높일 수 있습니다.
- 불변객체(immutable class)를 사용합니다.
- final키워드를 통해 선언한 변수와 객체는 여러스레드가 동시에 접근해도 동일한 값을 보장받을 수 있기 때문에 thread-safe합니다.
- 대표적인 불변객체는 String입니다.
- 공유 자원의 사용을 최소화합니다.
- 스레드별로 고유의 공간을 가지는 stack에 한정해 프로그래밍을 진행하면 안전성을 높일 수 있습니다.
- Atomic클래스를 활용합니다.
- non-blocking상태로 동기화 문제를 해결하는 방법입니다.
- CAS(Compare And Sweep) 알고리즘으로 동작합니다.
- 인자로 기존값과 변경할 값을 전달합니다.
- 기존값이 현재 메모리에 있는 값과 동일하다면 변경할 값을 반영시켜 변경해 줍니다.
- 기존값이 현재 메모리에 있는 값과 다르다면 변경하지 않고 돌려보냅니다.
- thread의 상태를 변경시키지 않기 때문에 synchronized보다 성능이 좋습니다.
- concurrent패키지들이 내부적으로 이러한 방법을 사용합니다.
- Volatile키워드를 활용합니다.
- volatile키워드가 붙어있는 객체는 CPU캐시가 아닌 메인 메모리(ram)에서 값을 참조합니다.
- 비가시성: Cache에 담아쓰기 때문에 다른 스레드의 캐시에 있는 변수가 어떤 상태인지 알 수 없는 상황입니다.
가시성이 보장되었다고 동시성이 보장되는 건 아닙니다.
- 단, 한 스레드만 Write를 하고 나머지 스레드는 Read만 하는 상황에서는 가시성이 보장되면 동시성도 보장됩니다.
- 과정
- 멀티 스레드 환경에서는 스레드마다 각자의 CPU캐시에 변수값을 저장해 둡니다.
- 메인 메모리에 변경된 변수를 캐시 메모리가 미처 반영하지 못했다면 캐시 메모리를 참조한 스레드는 잘못된 값을 가지게 됩니다.
- 이를 방지하기 위해 스레드가 캐시 메모리가 아닌 메인 메모리의 값을 직접 읽고쓰게 만드는 게 바로 volatile키워드 입니다.
- 직접 가져다쓰기 때문에 위와같은 문제가 발생하지 않습니다.
thread
thread 상태
- getState()메서드를 통해 스레드 상태를 알 수 있습니다.
- JVM은 스레드를
new
,runnable
,running
,waiting
,terminate
다섯가지 상태로 관리합니다.
new
- 스레드 객체가 생성 후, 아직 start() 메소드가 호출되지 않은 상태입니다.
runnable
- 실행 상태로 언제든지 갈 수 있는 상태입니다.
running
- 스케줄링으로 선택되어 CPU를 점유하고 run() 메소드를 실행한 상태입니다.
waiting
terminate
프로세스 상태
Created
- 작업(Job)을 커널에 등록한 상태입니다. PCB를 할당받으며 프로세스가 생성됩니다.
Running
- 메모리와 프로세서(CPU)를 할당받은 상태입니다.
- 실제로 작업을 실행하는 상태입니다.
- 프로세서를 뺐겨 Ready상태가 되는 걸 Preemption이라고 합니다.
- 추가적인 자원이 필요해(I/O를 하기 위해) blocked(asleep)상태가 되는 걸 Block(sleep)이라고 합니다.
Ready
- 프로세서 이외의 모든 자원을 할당받은 상태입니다.
- CPU만 할당받으면 즉시 실행될 수 있습니다.
Blocked(asleep)
- I/O를 기다리는 상태입니다.
- 메모리는 할당받았지만 프로세서와 기타자원이 없는 상태입니다.
Suspended
- 메모리를 할당받지 못한 상태입니다.
- memory image를 swap device에 저장하고 있습니다.
- Suspended상태에서 메모리를 받으면 Swap-in, 원래 상태에서 메모리를 뺐기면 Swap-out이 됩니다.
- Suspended Ready와 Suspended Block상태가 존재합니다.
기타
extends thread와 implements Runnable의 차이
- 별 차이 없습니다. 단지 extends와 implements의 차이(단일상속, 다중상속)일 뿐입니다.
CompletableFuture
- CompletableFutere은 람다기반 비동기식 병렬 프로그래밍 기법입니다.
- Future와 CompletionStage를 구현한 클래스입니다.
- 간단하게 비동기 처리의 결과를 가져올 수 있게 설계된 클래스 입니다.
데몬 스레드
- 자동으로 호출 및 종료됩니다.(사용자가 직접적으로 제어하지 않습니다)
- 사용자 스레드를 돕는 역할을 합니다.
프로세스 종료시점
- 사용자 스레드가 하나도 존재하지 않을 때 종료됩니다.
- 메인 스레드의 종료시점과는 무관합니다.
스레드 그룹
- 서로 관련된 스레드를 그룹으로 묶어서 관리하는 걸 의미합니다.
- 모든 스레드는 반드시 하나의 스레드 그룹에 포함되어야 합니다.
- 그룹을 지정하지 않고 생성한 스레드는 main스레드 그룹에 포함됩니다.
- 생성된 스레드는 자신을 생성한 스레드(부모 스레드)의 그룹과 우선순위를 상속받습니다.
References