1. 멀티스레드
- 실제 동시에 처리될 수 있는 프로세스의 개수는 CPU 코어의 개수와 동일한데, 이보다 많은 개수의 프로세스가 존재하기 때문에 모두 함께 동시에 처리할 수는 없다.
- 각 코어들은 아주 짧은 시간동안 여러 프로세스를 번갈아가며 처리하는 방식을 통해 동시에 동작하는 것처럼 보이게 한다.
- 이와 마찬가지로, 멀티스레딩이란 하나의 프로세스 안에 여러개의 스레드가 동시에 작업을 수행하는 것을 말한다.
- 스레드는 하나의 작업단위이다.
멀티스레드 적용하기 위한 조건
- 병행성(concurrency) : 다수의 스레드 생성방법 존재
- 동기화(synchronization) : 작업이 방해받지 않고 각 스레드의 동기화 방법 존재
- 통신(communication) : 서로 다른 스레드가 정보를 교환할 수 있는 방법이 존재
공유자원과 임계영역
- 공유자원 : 여러 스레드가 동시에 접근할 수 있는 자원
- 임계영역 : 공유자원들 중 여러 스레드가 동시에 접근했을 때 문제(경쟁상태)가 생길 수 있는 부분
- 임계영역이 문제가 발생하지 않도록 만족해야 하는 조건
- 상호 배제 : 하나의 스레드가 임계 영역에 들어가있다면 다른 스레드는 들어갈 수 없어야 한다.
- 진행 : 임계 영역에 들어간 스레드가 없는 상태에서, 들어가려하는 스레드가 여러개라면 어느 것이 들어갈지 결정해주어야 한다.
- 한정 대기 : 다른 스레드의 기아상태를 방지하기 위해, 한 번 임계 구역에 들어간 스레드는 다음 번 임계 영역에 들어갈 때 제한을 두어야 한다.
- 임계영역이 문제가 발생하지 않도록 만족해야 하는 조건
경쟁상태 (race condition)
- 둘 이상의 스레드가 공유자원을 병행적으로 읽거나 쓰는 동작을 할 때 타이밍이나 접근 순서에 따라 실행 결과가 달라지는 상황
- ex) Read - Modify - Write (30명의 수강생이 동시에 신청하게 된다면?), Check - then - act (수강신청 후에 숫자를 세서 30명 미만이면 폐강 위험 경고문을 출력하는 메소드를 동시에 100명이 요청한다면?)
원자성과 가시성
- 원자성 : 공유 자원에 대한 작업의 단위가 더이상 쪼갤 수 없는 하나의 연산인 것처럼 동작하는 것

각 작업마다 시간텀이 있기 때문에 경쟁상태가 생기는 것이다! 이러한 경쟁상태를 발생시키지 않기 위해서는 분리된 명령어를 하나로 모아주는 과정이 필요하다. 이것이 바로 원자성이다.
- 가시성
공유자원은 메인 메모리에 적재되어 있다. Thread는 동작하는 시점에 하나의 CPU를 점유하고 동작한다.
그런데 공유자원의 값이 메모리에만 존재하는 것이 아니라, CPU cache라는 영역에도 존재한다. 이는 CPU가 메인 메모리에서 값을 읽고쓰는 시간을 아끼기 위함이다. (스레드는 필요한 값을 메모리에서 읽어와서 캐시영역에 담아두고 캐시에 모든 연산을 반영한 다음에 메모리에 덮어쓰는 매커니즘을 가지고 있다.) 여기서 문제가발생한다. CPU의 cache의 값이 언제 메인 메모리로 옮겨갈지 모른다는 것이다. 이를 해결하는 것을 가시성이라 한다.

자바에서는 비 가시성 문제를 해결하기 위해 Volatile 이라는 기능을 제공한다. 변수를 선언할 때 해당 단어를 앞에 적어주면 된다. volatile로 선언된 변수를 CPU에서 연산을 하면 바로 메모리에 덮어씌운다. (CPU cache 사용하지 않는다.)
2. 동기화란
동기화
- 여러 스레드가 같은 프로세스 내의 자원을 공유하면서 작업할 때 서로의 작업이 다른 작업에 영향을 주기 때문에 멀티스레드 환경에서 동기화는 필수적이다.
- 스레드의 동기화를 위해선, 임계 영역(critical section)과 잠금(lock)을 활용한다. 임계영역을 지정하고, 임계영역을 가지고 있는 lock을 단 하나의 스레드에게만 빌려주는 개념으로 이루어져있다.
- 따라서 임계구역 안에서 수행할 코드가 완료되면, lock을 반납해줘야 한다.
- 즉, 동기화는 멀티스레드 환경에서 가시성과 원자성을 보장해주기 위함이다.
동기화 방법
- 임계 영역(critical section) : 공유 자원에 단 하나의 스레드만 접근하도록(하나의 프로세스에 속한 스레드만 가능)
- 뮤텍스(mutex) : 공유 자원에 단 하나의 스레드만 접근하도록(서로 다른 프로세스에 속한 스레드도 가능)
- 이벤트(event) : 특정한 사건 발생을 다른 스레드에게 알림
- 세마포어(semaphore) : 한정된 개수의 자원을 여러 스레드가 사용하려고 할 때 접근 제한
- 대기 가능 타이머(waitable timer) : 특정 시간이 되면 대기 중이던 스레드 깨움
3. 동기화 방식 - 블로킹
블로킹
- 특정 스레드가 작업을 수행하는 동안 다른 작업은 진행하지 않고 대기하는 방식
- ex) Moniter, Synchronized 키워드
Moniter
- 자바에서 동기화를 하기 위한 도구
- 배타동기는 Synchronized, 조건 동기는 wait() / notify() / notifyAll()
Synchronized 활용
- synchronized를 활용해 임계영역을 설정할 수 있다.
서로 다른 두 객체가 동기화 하지 않은 메소드를 같이 오버라이딩해서 이용하면, 두 스레드가 동시에 진행되므로 원하는 출력 값을 얻지 못한다. 이때 오버라이딩되는 부모 클래스의 메소드에 synchronized 키워드로 임계영역을 설정할 수 있다.
//synchronized : 스레드의 동기화. 공유 자원에 lock
public synchronized void saveMoney(int save){ // 입금
int m = money;
try{
Thread.sleep(2000); // 지연시간 2초
} catch (Exception e){
}
money = m + save;
System.out.println("입금 처리");
}
public synchronized void minusMoney(int minus){ // 출금
int m = money;
try{
Thread.sleep(3000); // 지연시간 3초
} catch (Exception e){
}
money = m - minus;
System.out.println("출금 완료");
}
동기화의 한계
- lock의 유무로 순차접근을 하기때문에 원자성과 가시성이 보장되지만, 나머지 스레드가 대기한다는점에서 성능저하가 발생할 수 있다.
- 데드락
- 둘 이상의 프로세스가 다른 프로세스가 점유하고 있는 자원을 서로 기다릴 때 무한 대기에 빠지는 상황
4. 동기화 방식 - 논블로킹
논블로킹
- 다른 스레드의 작업 여부와 상관없이 자신의 작업을 수행하는 방식
- ex) Atomic 타입
CAS (Compare and Set)
Atomic 타입
- 동시성을 보장하기 위해서 자바에서 제공하는 Wrapper class
- CAS(원자성) + Volatile (가시성) 보장
5. Synchronous vs Asynchronous
- Synchronous : 작업을 동시에 수행하거나, 동시에 끝나거나, 끝나는 동시에 시작함을 의미
- ex) 상사가 작성한 피드백을 바로 반영하여 자신의 일을 하는 사원
- Asynchronous : 시작, 종료가 일치하지 않으며 끝나는 동시에 시작을 하지 않음을 의미
- ex) 상사가 작성한 피드백은 나중에 처리하거나 하지 않고, 자신의 일을 하는 사원
- Synchronous 나 Asynchronous 모두 특정 스레드가 할 일을 하는 동안 자신은 기다리거나 혹은 자신의 일을 하거나 상관 없음
4가지 조합
- 블로킹/Sync
- 다른 작업이 시작되는 동안 동작하지 않고, 결과를 반환하면 해당 업무를 바로 처리한다.
- 사원이 상사에게 서류를 전달하면, 상사가 해당 서류를 다 읽을 때까지 기다리게하고(블로킹) 다 읽고나서 피드백의 결과를 사원은 바로 처리하게 된다.
- ex) 자바의 입력요청
- 논블로킹/Sync
- 다른 작업이 있어도 자신의 일을 동작하고, 중간중간 결과를 물어본다. 결과를 가지고 올 수 있을 때 가지고와서 같이 처리한다.
- 상사가 서류를 읽을 동안 사원은 다른 일을 처리하고, 중간중간 물어보다가, 피드백의 결과가 나오면 바로 처리한다.
- 블로킹/Async
- 다른 작업이 시작되는 동안 동작하지 않고, 결과가 반환되어도 해당 업무를 바로 처리하지 않아도 된다.
- 논블로킹/Async
- 다른 작업이 있어도 자신의 일을 동작하고, 결과가 반환되어도 바로 처리하지 않는다. 자신의 일이 끝나면 그때서야 처리한다.
- ex) 자바스크립트에서 API 요청을 하고 자신의 작업을 하다가 콜백을 통해서 추가적인 작업을 처리할 때
정리
- Blocking vs Non-Blocking : 제어의 관점
- Sync vs Async : 순서와 결과(처리)의 관점
출저
https://www.youtube.com/watch?v=ktWcieiNzKs
https://www.youtube.com/watch?v=oEIoqGd-Sns
'JAVA' 카테고리의 다른 글
[JAVA] 객체 생성 전략 - 정적 팩토리 메서드 & 빌더 패턴 (0) | 2024.05.08 |
---|---|
자료구조 Collection - List, Set, Map (0) | 2023.06.02 |
[JAVA] Thread 란 (0) | 2023.02.06 |
[JAVA] GC 동작 알고리즘 (0) | 2023.02.05 |
[JAVA] Call by Value & Call by Reference (0) | 2023.02.05 |