*synchronized**
는 Java에서 동기화(Synchronization)를 구현하는 키워드로, 멀티스레드 환경에서 공유 자원에 대한 동시 접근을 제어하는 데 사용됩니다. Java는 멀티스레딩을 지원하는 언어로, 여러 스레드가 동시에 실행될 수 있습니다. 이러한 환경에서는 여러 스레드가 동시에 동일한 자원(예: 변수, 객체의 메서드 등)에 접근하려고 할 때, 데이터의 일관성이 깨지거나 예상치 못한 결과가 발생할 수 있습니다. 이를 방지하기 위해 동기화가 필요합니다.
1. synchronized
키워드의 역할
synchronized
키워드는 특정 코드 블록이나 메서드를 임계 영역(Critical Section)으로 지정하여, 한 번에 하나의 스레드만 이 영역에 진입하여 실행할 수 있도록 보장합니다. 즉, 한 스레드가 임계 영역을 실행하고 있는 동안 다른 스레드는 대기 상태가 됩니다.2. synchronized
키워드 사용 방법
synchronized
는 두 가지 방법으로 사용할 수 있습니다:1) synchronized 메서드
메서드 전체를 임계 영역으로 설정하는 방법입니다. 해당 메서드가 실행되는 동안에는 다른 스레드가 이 객체의 다른 synchronized 메서드에 접근할 수 없습니다.
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
위의 예제에서
increment()
와 getCount()
메서드는 synchronized
로 선언되어 있습니다. 따라서 한 스레드가 increment()
메서드를 실행하는 동안 다른 스레드는 increment()
나 getCount()
를 실행할 수 없습니다.2) synchronized 블록
코드의 특정 부분만 동기화가 필요할 때, 메서드 전체가 아닌 코드 블록을 임계 영역으로 설정할 수 있습니다. 이 경우
synchronized
키워드는 객체의 모니터 락을 명시적으로 지정할 수 있습니다.public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
}
이 예제에서는
lock
객체를 사용하여 특정 블록만 동기화합니다. increment()
와 getCount()
메서드에서 동일한 lock
객체를 사용하기 때문에, 한 스레드가 increment()
메서드 내의 동기화 블록을 실행 중이면 다른 스레드는 getCount()
메서드의 동기화 블록에 진입할 수 없습니다.3. 동기화의 장점과 단점
장점:
- 데이터 일관성 보장: 여러 스레드가 공유 자원에 접근할 때 발생할 수 있는 데이터 불일치 문제를 방지할 수 있습니다.
- 스레드 안전성 확보: 동기화된 메서드나 블록을 통해 공유 자원에 대한 스레드 안전성을 보장할 수 있습니다.
단점:
- 성능 저하: 동기화는 대기 시간이 발생할 수 있어 프로그램의 성능을 저하시킬 수 있습니다. 특히 많은 스레드가 동기화된 영역에 접근하려고 할 때 성능 저하가 두드러질 수 있습니다.
- 교착 상태(Deadlock): 동기화된 코드에서 교착 상태가 발생할 수 있습니다. 예를 들어, 두 개의 스레드가 서로 다른 락을 요청하는 과정에서 서로가 요청하는 락을 이미 다른 스레드가 점유하고 있을 때 교착 상태가 발생할 수 있습니다.
4. 주의할 점
- 동기화 범위 최소화: 성능 문제를 피하기 위해, 꼭 필요한 부분에만 동기화를 적용하는 것이 중요합니다. 동기화 범위가 클수록 성능에 악영향을 줄 수 있습니다.
- 객체 레벨 vs 클래스 레벨 동기화: 인스턴스 메서드에
synchronized
를 적용하면 해당 인스턴스에 대한 동기화가 이루어지지만, 클래스 메서드에 적용하면 해당 클래스 전체에 대한 동기화가 이루어집니다. 클래스 레벨 동기화는 클래스의 모든 인스턴스에 대해 동기화가 적용되므로 주의해서 사용해야 합니다.
5. 실습 예제
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedExample example = new SynchronizedExample();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + example.getCount());
}
}
이 코드에서 두 개의 스레드가 동시에
increment()
메서드를 호출합니다. synchronized
키워드 덕분에 count
변수에 대한 동시 접근이 제어되어, 최종 결과는 2000
이 됩니다. 만약 synchronized
를 제거하면, 결과가 예측 불가능한 값이 될 수 있습니다.결론
synchronized
는 Java에서 멀티스레드 프로그래밍 시 중요한 역할을 하는 키워드로, 동기화를 통해 스레드 간의 자원 접근을 제어하고 데이터 일관성을 유지할 수 있습니다. 하지만 과도한 동기화는 성능 저하를 초래할 수 있기 때문에, 필요에 맞게 신중하게 사용하는 것이 중요합니다.Share article