주의 사항 : 힙 영역에 있는 데이터는 모든 스레드에서 접근하여 사용할 수 있다. 이 때 가장 큰 문제점으로는 원자적 작업이 아닌 경우 개발자가 생각한 결과가 안 나올 수 있다. 이처럼 공유되는 영역에 접근하고 thread-safe하지 않는다면 동기화를 진행해줘야 한다.
해결법
- synchronized 키워드 사용
- 이 키워드를 통해 여러 스레드가 코드 블록이나 메소드에 동시에 액세스 할 수 없도록 해주는 락킹 메커니즘이다.
- 소괄호 안에 지정한 객체를 잠금 키로서 사용하는 것 같다. 한 스레드가 접근했으면 객체의 어떤 상태가 1로 변하는 느낌(gpt 답변 인증 받음)
/*
첫 번째 방법
이 키워드는 객체마다 각각 적용되기 때문에 객체 안에 synchronized 메소드가 2개 있고,
threadA가 그 중 한 메소드를 호출했을 때 threadB는 두 synchronized 메소드를 호출 할 수 없다.
이 키워드가 없는 메소드는 동시에 호출할 수 있다.
*/
private static class InventoryCounter {
private int items = 0;
public synchronized void increment() {
items++;
}
public synchronized void decrement() {
items--;
}
public int getItems() {
return items;
}
}
/*
두 번째 방법 (추천)
블록만을 동기화하여 메소드의 나머지 부분은 동시에 수행할 수 있도록 할 수 있다. -> 성능 최적화
lock에 대해 여러 개가 있을 경우 메소드 별로 락이 다를 경우 한 메소드에서는 threadA가(threadB, C 접근 X)
다른 메소드에서는 threadB(threadA, C 접근 X)와 같이 유연성을 높일 수 있다.
결국 위의 첫 번째 방법은 아래와 같은 것이다.
public void increment() {
synchronized (this) {
items++;
}
}
따라서 같은 객체에 대해 락을 걸기 때문에 다른 synchronized 메소드가 안 됐던 것이다.
*/
private static class InventoryCounter {
private int items = 0;
//락용 객체 생성
private final Object lock = new Object();
public void increment() {
synchronized (this.lock) {
items++;
}
}
public void decrement() {
synchronized (this.lock) {
items--;
}
}
public int getItems() {
return items;
}
}
여기서는 위의 첫번째 방법과 같은 형식이다. 다르게 하려면 lock용 객체를 하나 더 만들어야 됨.
Tip) synchronized() 인자에는 객체마다 다르게 설정할 수 있으므로 특정 부분 별로 동시성을 따로 챙겨줄 수 있다. 예를 들어 userId 별로 2개 이상 존재하면 안 되는 것이 있을 때는 Map 자료 구조에 userId 별 Object 객체를 생성해주고 이를 이용해서 락을 걸어주면 된다.
'JAVA > 멀티스레딩' 카테고리의 다른 글
| 락킹 심화 (0) | 2024.08.29 |
|---|---|
| 병행성 문제와 솔루션 (0) | 2024.08.29 |
| Thread Pooling (0) | 2024.08.28 |
| 스레드 조정 (0) | 2024.08.28 |
| 스레드 생성 (0) | 2024.08.27 |