Java에서 volatile과 synchronized는 멀티스레딩 환경에서 안전하게 데이터를 접근하고 수정하기 위한 동시성 제어 키워드이다.
이 두 키워드는 각각 다른 방식으로 동기화를 제공한다.
volatile
volatile 키워드는 변수의 값을 모든 스레드가 항상 최신 상태로 읽을 수 있도록 보장한다.
자바 메모리 모델에서 각 스레드는 자신의 캐시를 사용하여 변수를 읽고 쓸 수 있다.
volatile로 선언된 변수는 각 스레드의 캐시에 저장되지 않고 항상 주 메모리에서 읽고 쓰기 때문에 여러 스레드가 동시에 접근하더라도 일관된 값을 보장한다.
주요 특징
변수의 가시성 보장
volatile로 선언된 변수는 각 스레드가 해당 변수의 최신 값을 볼 수 있도록 한다.
원자성 보장 불가
volatile은 변수의 읽기/쓰기의 원자성을 보장하지 않는다.
복합 연산(예: i++)에 대해 안전하지 않다.
예제
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
new Thread(() -> {
while (!flag) {
// flag가 true가 될 때까지 대기
}
System.out.println("Flag has been set to true!");
}).start();
new Thread(() -> {
try {
Thread.sleep(1000); // 1초 대기
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
flag = true; // flag 값을 true로 설정
System.out.println("Flag set to true.");
}).start();
}
}
synchronized
synchronized 키워드는 한 번에 하나의 스레드만 특정 블록이나 메서드에 접근할 수 있도록 하는 방법을 제공한다.
이는 코드의 동시성을 제어하여 여러 스레드가 동시에 같은 자원에 접근할 때 발생할 수 있는 문제를 예방한다.
주요 특징
상호 배제 (Mutual Exclusion)
특정 블록이나 메서드가 한 스레드에 의해 사용되고 있을 때 다른 스레드는 해당 블록이나 메서드에 접근할 수 없다
가시성 보장
synchronized 블록 안의 모든 변수는 해당 블록을 나가는 시점에 메인 메모리에 쓰여지고, 다른 스레드는 해당 블록에 들어올 때 메인 메모리에서 최신 값을 읽는다.
예제
public class SynchronizedExample {
private int count = 0;
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
example.doWork();
}
public synchronized void increment() {
count++;
}
public void doWork() {
Thread thread1 = new Thread(this::increment);
Thread thread2 = new Thread(this::increment);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Count: " + count);
}
}
volatile vs synchronized
특징 volatile synchronized
가시성 | 모든 스레드가 항상 최신 값을 읽을 수 있도록 보장 | 블록이나 메서드 내의 변수 값이 최신 상태로 유지되도록 보장 |
원자성 | 단순 읽기 및 쓰기 연산만 원자성 보장 | 모든 연산의 원자성 보장 |
사용 사례 | 단순한 플래그나 상태 값 변경 | 복합 연산이나 공유 자원 보호 |
성능 | 비용이 적음 | 비용이 높음 (잠금과 잠금 해제) |
사용법 | 변수에 직접 적용 | 메서드나 블록에 적용 |
volatile
변수가 변경될 때마다 모든 스레드가 즉시 최신 값을 볼 수 있도록 한다
간단한 읽기/쓰기 작업에 적합하다
복합 연산에는 적합하지 않다 (예: i++)
synchronized
블록 또는 메서드에 대해 상호 배제를 제공한다
원자성을 보장한다
가시성을 보장한다
성능 오버헤드가 있을 수 있다
따라서, 두 키워드는 각각의 용도에 맞게 사용되어야 하며, 특정 상황에 맞는 동기화 메커니즘을 선택하는 것이 중요하다.
volatile은 간단한 변수의 가시성 보장에 사용하고, synchronized는 더 복잡한 동기화가 필요한 경우에 사용한다
'Job Interview Prep' 카테고리의 다른 글
String과 StringBuilder, StringBuffer의 차이 (0) | 2024.05.31 |
---|---|
제네릭(Generic)이란 (0) | 2024.05.31 |
JavaBean (1) | 2024.05.31 |
Java의 다형성(Polymorphism) (0) | 2024.05.31 |
Docker vs Kubernetes / Docker Compose vs Docker Swarm vs Kubernetes (1) | 2024.05.28 |