
Java에서 synchronized 키워드는 멀티스레드 환경에서 데이터를 안전하게 공유하고 동기화하기 위해 사용됩니다. 여러 스레드가 동시에 접근할 수 있는 자원(주로 변수나 메서드)에 대한 동시 접근을 제어하여, 데이터 불일치나 경쟁 상태(race condition)를 방지하는 역할을 합니다.
Synchronized 키워드의 사용 위치
(1) 메서드 선언부에 사용
- 메서드 선언부에 synchronized를 붙이면, 해당 메서드를 호출하는 스레드가 그 메서드를 실행할 때 동기화를 보장합니다.
- 인스턴스 메서드에서 사용: 해당 메서드가 호출될 때마다 인스턴스(객체)에 대한 락이 걸립니다. 여러 스레드가 같은 객체의 메서드를 호출할 때, 하나의 스레드만 메서드를 실행할 수 있도록 보장합니다.
- 클래스 메서드에서 사용: static 메서드에 synchronized를 붙이면 클래스 객체에 대한 락이 걸립니다. 모든 인스턴스가 공유하는 자원에 대해 동기화가 이루어집니다.
public synchronized void exampleMethod() {
// 동기화된 작업
}
(2) 블록 내부에 사용
- synchronized는 특정 코드 블록을 동기화할 수 있도록 제한적으로 사용할 수 있습니다. 이 방법은 메서드 전체를 동기화하는 대신, 특정 부분에만 동기화를 적용할 수 있어 성능 면에서 유리할 수 있습니다.
- 동기화 대상 객체를 명시적으로 지정하여 여러 객체 간에 동기화를 세밀하게 조정할 수 있습니다.
public void exampleMethod() {
synchronized(this) {
// 동기화된 작업
}
}
(3) 객체에 대한 동기화
- 객체에 대한 동기화를 명시적으로 지정하는 경우, synchronized 키워드를 사용하여 다른 객체에 대한 락을 지정할 수 있습니다. 이 경우 특정 객체에만 락을 걸기 때문에 더 세밀한 제어가 가능합니다.
public void exampleMethod() {
synchronized (someObject) {
// someObject에 대한 동기화된 작업
}
}
Synchronized 단점
- 성능 문제:
- 동기화는 락을 획득하는 과정에서 다른 스레드들이 대기하게 되므로, 여러 스레드가 경쟁하는 상황에서는 성능 저하가 발생할 수 있습니다.
- 특히 동기화가 자주 일어나는 경우, 병목 현상이 발생할 수 있습니다.
- 데드락(Deadlock) 발생 가능성:
- 여러 스레드가 서로 다른 순서로 자원을 요청할 때 교착 상태에 빠질 수 있습니다. 이를 방지하려면 락의 순서를 잘 설정해야 합니다.
- 컨텍스트 스위칭:
- 스레드 간의 컨텍스트 스위칭이 발생할 수 있으며, 이는 CPU 사이클을 낭비하게 됩니다.
따라서 synchronized를 사용할 때는 필요한 부분에만 최소화하여 사용하는 것이 효율적입니다. 과도한 동기화는 성능을 크게 저하시킬 수 있습니다.
Synchronized를 대체할 수 있는 자바의 다른 동기화 기법
(1) ReentrantLock
- ReentrantLock은 java.util.concurrent.locks 패키지에 포함된 클래스입니다. synchronized와 유사하지만 더 유연한 제어를 제공합니다. 예를 들어, 락을 시도하는 동안 타임아웃을 설정하거나, 락을 여러 번 획득할 수 있습니다.
- ReentrantLock은 락을 명시적으로 해제할 수 있어, 예외 발생 시에도 락을 해제할 수 있는 장점이 있습니다.
Lock lock = new ReentrantLock();
lock.lock();
try {
// 동기화된 작업
} finally {
lock.unlock();
}
(2) ReadWriteLock
- ReadWriteLock은 데이터를 읽을 때와 쓸 때 락을 구분하여 성능을 최적화하는 방법입니다. 여러 스레드가 동시에 데이터를 읽을 수 있지만, 쓰기 작업이 발생하면 그때는 독점적으로 락을 획득해야 합니다.
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
(3)Atomic 클래스들
- AtomicInteger, AtomicLong, AtomicReference와 같은 클래스를 사용하여, 동기화 없이도 안전하게 값을 갱신할 수 있습니다. 이들은 내부적으로 CAS(Compare-And-Swap) 알고리즘을 사용하여 동기화를 처리합니다.
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
(4)Executors 및 ExecutorService
- 스레드 풀을 사용하여 동시성 작업을 효율적으로 관리하고, ExecutorService와 같은 인터페이스를 활용하여 작업을 병렬로 수행하면서도 적절한 동기화를 할 수 있습니다.
Thread Local에 대해
ThreadLocal은 각 스레드마다 독립적인 변수를 저장하고 접근할 수 있게 해주는 클래스입니다. 일반적으로 멀티스레딩 환경에서 각 스레드가 자신만의 변수를 가질 수 있도록 보장할 때 유용하게 사용됩니다.
- 사용 방법: ThreadLocal을 사용하면 각 스레드는 자신만의 데이터를 저장하고, 다른 스레드는 해당 데이터를 접근할 수 없습니다. 이를 통해 멀티스레드 환경에서 데이터 충돌을 방지할 수 있습니다.
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public void exampleMethod() {
threadLocal.set(threadLocal.get() + 1);
}
- 장점:
- 멀티스레드 환경에서 각 스레드마다 독립적인 데이터를 사용할 수 있어 동기화의 필요성이 줄어듭니다.
- 성능이 매우 우수하며, 스레드 간 데이터 충돌을 방지할 수 있습니다.
- 단점:
- ThreadLocal에 저장된 값은 스레드가 종료될 때 자동으로 삭제되지 않기 때문에, 메모리 누수(Memory Leak) 문제를 방지하기 위해 수동으로 정리할 필요가 있습니다.

synchronized 키워드는 멀티스레드 환경에서 자원의 안전한 공유를 보장합니다. 효율적인 코드 작성 측면에서 사용 시 성능 저하나 데드락이 나타날 수 있습니다.
'Back_End > JAVA' 카테고리의 다른 글
자바(JAVA)의 Default Interface란? (1) | 2025.02.28 |
---|---|
자바(JAVA) DAO, DTO, VO 란? (0) | 2025.02.26 |
자바(Java) Optional 이란? (0) | 2025.02.14 |
얼리 리턴 패턴(Early Return Pattern) 이란? (0) | 2025.02.14 |
Java의 Comparator vs Comparable (1) | 2025.02.14 |