멀티 스레드 환경에서 동시성 문제는 여러 스레드가 동시에 실행되면서 공유자원에 접근하거나 수정할 때 발생하는 문제 동시성 문제는 주로 데이터 불일치, 레이스 컨디션, 데드락 등의 형태로 나타난다.
동시성 문제의 유형
- 데이터 불일치: 여러 스레드가 동시에 데이터를 읽고 수정하면서 데이터의 일관성이 깨지는 현상.
- 레이스 컨디션: 여러 스레드가 경쟁적으로 자원에 접근하면서 발생하는 문제로, 예기치 않은 실행결과가 나올 수 있다.
- 데드락: 두 개 이상의 스레드가 서로가 가지고 있는 자원을 기다리면서 무한 대기상태에 빠지는 문제}
Thread-safe
여러 스레드가 동시에 접근하거나 수정할 수 있는 코드나 객체가
데이터의 일관성과 안정성을 유지하도록 안전하게 설계된 것을 의미한다.
동시성 문제 해결 방법
1. 동기화( Synchronization)
공유자원에 대한 접근을 제어하여 데이터의 일관성을 유지하는 방법으로 Lock을 걸게 되면 스레드의 공유자원에 대한 독점을 허용하기 때문에 다른 스레드가 자원에 접근하지 못하고 대기하게 된다. 이는 여러 스레드가 공유자원에 동시에 접근하지 못하므로 동시성 문제를 막을 수 있지만, 하나의 스레드만 자원을 사용하므로 병렬성은 낮아지게 된다.
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
2. Lock 사용
synchronized키워드를 사용하는 암시적 Lock과는 달리 Lock 객체를 사용하여 더 정교한 제어가 가능
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
3. Atomic 변수 사용
java.util.concurrent.atomic 패키지의 Atomic 클래스들을 사용하여 스레드 안전하게 값을 수정할 수 있다
Atomic 클래스들은 Compare and Swap(CAS) 기반으로 되어 있어 스레드에 안전하다. Compare and Swap(CAS)란 변수의 값을 변경하기 전에 기존의 값이 내가 예상한 값인지 확인하여 같은 경우에만 값을 할당하는 알고리즘 기법이다. (값을 할당하기 전 한 번 더 검사함)
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
4. Concurrent Collections 사용
java.util.concurrent 패키지는 동시성 문제를 해결하기 위한 다양한 클래스와 인터페이스를 제공한다. ConcurrentHashMap, CopyOnWriteArrayList 등의 클래스들이 있
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapExample {
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
public void put(String key, Integer value) {
map.put(key, value);
}
public Integer get(String key) {
return map.get(key);
}
}
5. 데드락 방지
잠금 순서를 정해 데드락을 예방하거나, 타임아웃을 설정하여 데드락에 빠지지 않도록 할 수 있다.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class DeadlockExample {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
public void method1() {
try {
if(lock1.tryLock(10, TimeUnit.SECONDS)) {
try {
if(lock2.tryLock(10, TimeUnit.SECONDS)) {
try {
// Critical section
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void method2() {
try {
if(lock2.tryLock(10, TimeUnit.SECONDS)) {
try {
if(lock1.tryLock(10, TimeUnit.SECONDS)) {
try {
// Critical section
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[결과]
동시성 문제는 주로 데이터 불일치, 레이스 컨디션, 데드락 등의 형태로 나타나고 멀티스레드 환경에서 동시성 문제를 해결하기 위해서는 주로 동기화, Lock 객체, Atomic 변수, Concurrent Collections 등을 사용한다.
[느낀 점]
불변객체를 사용하여 동시성 문제를 해결할 수도 있지만 객체를 생성하는 비용이 굉장히 크기 때문에 성능상 문제가 발생할 수 있므로 내용을 제외하였다.
참조 사이트
https://everydayyy.tistory.com/154
[Java] 멀티스레드 환경에서의 동시성 문제와 대책
스레드란? 프로세스는 실행중인 프로그램이란 뜻이다. 프로그램이 실제로 실행되어, 메모리나 CPU와 같은 자원을 할당 받으면 이를 프로세스라고 부른다. 스레드는 프로세스 내에서 실제로 작업
everydayyy.tistory.com
[Java] 멀티쓰레드 동기화 - synchronized
쓰레드의 동기화 씽글쓰레드의 경우에는 하나의 프로세스 내에서 하나의 쓰레드만 작업하기 때문에 프로세스의 자원에 대한 문제가 딱히 없다. 하지만 멀티쓰레드의 경우 여러개의 쓰레드가
yeoonjae.tistory.com
'Java' 카테고리의 다른 글
:JAR과 WAR과 웹을 배포하는 순서 (0) | 2024.08.19 |
---|---|
자바의 버전별 특징 java8,11, 17 (0) | 2024.08.05 |
:프로세스와 스레드 (0) | 2024.08.01 |
예외가 생겼으니 처리를하려면: 예외처리(Exception Handling) 3가지 방법 (0) | 2024.07.07 |
둘이 같은 게 아닌가?(2): 인터페이스와 추상클래스 (0) | 2024.07.07 |