- 한 cpu에서 동시에 여러 작업을 하는 것처럼 보이게 만드는 것
- 동시에 실행되는 것 같이 보이는 것
- 싱글 코어에서 멀티 쓰레드를 동작 시키는 방식
- 한 번에 많은 것을 처리
- 논리적인 개념
- concurrently ⭕ simultaneously ❌
- concurrently : N 개의 task 의 실행 시간이 타임라인 상에서 겹칠 수 있다
- simultaneously : 우리가 일반적으로 사용하는 ’동시에’ 라는 단어의 맥락
context switching
이 일어나면서 번갈아서 실행된다
- 다중 cpu에서 작업이 병렬적으로 실행된다. 하나의 cpu당 코드가 실행된다
- 실제로 동시에 여러 작업이 처리되는 것
- 멀티 코어에서 멀티 쓰레드를 동작시키는 방식
- 한 번에 많은 일을 처리
- 물리적인 개념
- concurrently ⭕ simultaneously ⭕
데이터 병렬성(Data parallelism)
과작업 병렬성(Task parallelism)
으로 구분된다
병렬 스트림
이 데이터 병렬성을 구현한 것이다웹 서버
는 각각의 브라우저에서 요청한 내용을 개별 쓰레드에서 병렬 처리한다동시성 > 병렬성
CPU
가 작업처리시에,
RAM
에 있는 데이터 일부를 고속 저장 장치CPU Cache Memory
로 읽어들인다.CPU
가 작업을 마치고 데이터를RAM
에 저장할 때,
CPU Cache Memory
에서RAM
으로 쓰기 작업을 수행한다
- 👉 이 말은
CPU
에서CPU Cache Memory
로 쓰기 작업을 수행하여도,
RAM
으로 쓰기작업을 바로 수행할 필요가 없다는 의미
CPU
와RAM
중간에 위치한CPU Cache Memory
의 병렬성으로 두 가지 문제가 발생한다
👉가시성(visibility) 문제
&동시 접근 문제
하나의 스레드에서 공유 자원(변수, 객체)을 수정한 결과가 다른 스레드에게 보이지 않을 수 있다
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested)
i++;
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
- 메인 스레드가 1초 sleep 후
stopRequested = true;
을 실행하여backgroundThread
스레드가 반복문을 빠져나올것 처럼 보이지만 아니다.
- CPU1에서 수행된 스레드 =
backgroundThread
CPU2에서 수행된 스레드 =mainThread
일때,
- ✍
mainThread
에서
CPU Cache Memory 2
와RAM
에 공유 변수인stopRequested
를true
로 쓰기 작업을 완료했지만- ✍
backgroundThread
에서
CPU Cache Memory 1
에서 읽은 여전히 업데이트 되지 않은stopRequested
값을 사용한다.
👉mainThread
가 수정한 값을backgroundThread
가 언제쯤에나 보게 될지 보증할 수 없다 (가시성 문제)
volatile
로 선언된 변수에 대해서는CPU Cache Memory
를 거치지 않고RAM
으로 직접 읽고 쓰는 작업을 수행하게 된다.
👉 stopRequested 변수를 volatile로 선언하면private volatile boolean stopRequested;
변수 값 불일치 문제를 해결 할 수 있다.
- Multi Thread 환경에서
- 하나의 Thread만 read & write하고 나머지 Thread가 read하는 상황에서 적합하다
- 여러 Thread가 write하는 상황에서는 적합하지 않는다
👉 여러 Thread가 write하는 상황에서는synchronized
를 통해 변수 read & write의 원자성(atomic)을 보장해야한다CPU Cache
보다Main Memory
가 비용이 더 크기 때문에 변수 값 일치을 보장해야 하는 경우에만volatile
사용하는 것이 좋다
여러 스레드에서 공유 자원에 동시에 접근하여 변경했을 때 문제가 발생할 수 있다
public class IncremantThread {
private static int count;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
for (int i = 0; i < 10000000; ++i) {
++count;
}
});
backgroundThread.start();
for (int i = 0; i < 10000000; ++i) {
++count;
}
TimeUnit.SECONDS.sleep(5);
System.out.println(count);
}
}
- 5초후에 결과 값
count
가20000000
으로 출력되지 않는다
- CPU1에서 수행된 스레드 =
backgroundThread
CPU2에서 수행된 스레드 =mainThread
일때,
count
변수의 값이2
라고 가정해보자.
- ✍
mainThread
와backgroundThread
에서 동시에
CPU Cache Memory
로count
값을 읽어오게 된다.- 두 스레드에서
count
값을 1 증가시켜3
이란 값을 각각의CPU Cache Memory
에 저장하게 된다.- 👉그 후, 두
CPU Cache Memory
에 있는count
값이RAM
에 저장이 된다면
3
이란 값이 연속으로 중복 저장된다 (동시 접근 문제)
lock
을 이용하여 스레드가 공유 자원에 접근시 하나의 스레드만 공유 자원에 접근할 수 있도록 한다
public class IncremantThread {
private static int count;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
for (int i = 0; i < 10000000; ++i) {
increment();
}
});
backgroundThread.start();
for (int i = 0; i < 10000000; ++i) {
increment();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(count);
}
// synchronized 키워드
private static synchronized void increment() {
++count;
}
}
synchrozied
는 가시성의 문제도 해결한다.volatile
은 동시 접근의 문제를 해결하지 못 한다.