문제 1 - 공유 자원
다음 코드의 결과는 20000이 되어야 한다. 이 코드의 문제점을 찾아서 해결해라. 이 코드에서 다른 부분은 변경하면 안 되고,`Counter` 클래스 내부만 수정해야 한다.


정답 코드

- count는 여러 스레드가 함께 사용하는 공유 자원이다.

단일 스레드가 공유 자원에 접근하는 상황
count 값이 0이라고 가정하겠다.
- count의 값을 읽는다. count 값은 0이다.
- 읽은 count의 값에 1을 더한다. 0+1=1이다.
- 더한 결과를count에 다시 저장한다. count 값은 1이다.
이처럼 단일 스레드가 공유 자원에 접근하는 경우는 아무런 문제가 없다.
여러 스레드가 공유 자원에 함께 접근하는 상황
count 값이 0이라고 가정하겠다. 2개의 스레드가 동시에 increment() 메서드를 호출한다.
- 순서 1
- 스레드 1: count의 값을 읽는다. count 값은 0이다.
- 스레드 2: count의 값을 읽는다. count 값은 0이다.
- 순서 2
- 스레드 1: 읽은 count의 값에 1을 더한다. 0 + 1 = 1이다.
- 스레드 2: 읽은 count의 값에 1을 더한다. 0 + 1 = 1이다.
- 순서 3
- 스레드 1: 더한 결과를 count에 다시 저장한다. count 값은 1이다.
- 스레드 2: 더한 결과를 count에 다시 저장한다. count 값은 1이다.
스레드 2개가 increment()를 호출하기 때문에 기대하는 count의 결과는 2가 되어야 한다. 하지만 둘이 동시에 실행되기 때문에, 처음에둘 다 count의값을 0으로 읽었다.
여기서 잘 보면 count의 값을 읽어서 계산하는 부분과 그 결과를 count에 다시 넣는 부분으로 나누어져 있다. 따라서 여러 스레드가 동시에 실행되면 지금과 같은 문제가 발생할 수 있다. 따라서 synchronized 키워드를 사용해서 한 번에 하나의 스레드만 실행할 수 있도록, 안전한 임계 영역을 만들어야 한다.
문제 2 - 지역 변수의 공유
다음 코드에서 MyTask의 run() 메서드는 두 스레드에서 동시에 실행한다. 다음 코드의 실행 결과를예측해 보자.그리고 localValue 지역 변수에 동시성 문제가 발생하는지 하지 않는지생각해 보자.



- localValue는 지역 변수이다.
- 스택 영역은 각각의 스레드가 가지는 별도의 메모리 공간이다. 이 메모리 공간은 다른 스레드와 공유하지 않는다.
- 지역 변수는 스레드의 개별 저장 공간인 스택 영역에 생성된다.
- 따라서 지역 변수는 절대로! 다른 스레드와 공유되지 않는다!
- 이런 이유로 지역 변수는 동기화에 대한 걱정을 하지 않아도 된다.
- 여기에 synchronized 를 사용하면 아무 이득도 얻을 수 없다. 성능만 느려진다!
- 지역 변수를 제외한, 인스턴스의 멤버 변수(필드), 클래스 변수 등은 공유될 수 있다.
문제 3 - final 필드
다음에서 value 필드(멤버 변수)는 공유되는 값이다. 멀티스레드 상황에서 문제가 될 수 있을까?
class Immutable {
private final int value;
public Immutable(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
- 여러 스레드가 공유 자원에 접근하는 것 자체는 사실문제가 되지 않는다.
- 진짜 문제는 공유 자원을 사용하는 중간에 다른 스레드가 공유 자원의 값을 변경해 버리기 때문에 발생한다. 결국 변경이 문제가 되는 것이다.
- 여러 스레드가 접근 가능한 공유 자원이라도 그 값을 아무도 변경할 수 없다면 문제 되지 않는다. 이 경우 모든 스레드가 항상 같은 값을 읽기 때문이다.
- 필드에 final 이 붙으면 어떤 스레드도 값을 변경할 수 없다. 따라서 멀티스레드 상황에 문제없는 안전한 공유 자원이 된다.
'멀티스레드와 동시성' 카테고리의 다른 글
| Ch07. 고급 동기화(concurrent.Lock) - LockSupport (0) | 2024.08.15 |
|---|---|
| Ch06. 동기화 - Synchronized 정리 (0) | 2024.08.12 |
| Ch06. 동기화 - 출금 (0) | 2024.08.11 |
| Ch06. 동기화 - synchronized (0) | 2024.08.11 |
| Ch06. 동기화 - 임계 영역 (0) | 2024.08.10 |