Java 동기화 블록(Synchronized Block)이란?
동기화 블록(synchronized block)은 메서드 전체가 아닌, 특정 리소스에 대해서만 동기화를 수행할 수 있도록 지정한 코드 영역입니다. 공유 자원에 접근하는 객체에 잠금(lock)을 걸어 여러 스레드가 동시에 해당 자원을 수정하는 것을 방지하며, synchronized 메서드보다 잠금 범위가 작기 때문에 불필요한 대기를 줄여 성능 면에서 유리합니다.
기본 문법
synchronized(object) {
// 동기화가 필요한 코드
}
여기서 object는 동기화 대상이 되는 객체에 대한 참조입니다. 동기화 블록을 사용하면 해당 객체의 메서드 호출은 현재 스레드가 그 객체의 모니터(monitor)를 성공적으로 획득한 이후에만 실행될 수 있습니다. 즉, 한 번에 하나의 스레드만 블록 안의 코드를 실행하게 됩니다.
예제: 티켓 예약 시스템
아래 예제는 좌석이 2개뿐인 티켓 카운터에서 두 명의 사용자가 각각 2석씩 예약을 시도하는 상황입니다. synchronized 블록 덕분에 두 스레드가 동시에 좌석 정보를 변경해 발생할 수 있는 데이터 불일치(race condition)를 막을 수 있습니다.
class TicketCounter {
int availableSeats = 2;
void bookTicket(String name, int numberOfSeats) {
if((availableSeats >= numberOfSeats) && (numberOfSeats > 0)) {
System.out.println(name + " : " + numberOfSeats + " Seats Booking Success");
availableSeats -= numberOfSeats;
} else {
System.out.println(name + " : Seats Not Available");
}
}
}
class TicketBookingThread extends Thread {
TicketCounter tc;
String name;
int seats;
TicketBookingThread(TicketCounter tc, String name, int seats) {
this.tc = tc;
this.name = name;
this.seats = seats;
}
public void run() {
synchronized(tc) { // 동기화 블록
tc.bookTicket(name, seats);
}
}
}
public class SynchronizedBlockTest {
public static void main(String[] args) {
TicketCounter tc = new TicketCounter();
TicketBookingThread t1 = new TicketBookingThread(tc, "Adithya", 2);
TicketBookingThread t2 = new TicketBookingThread(tc, "Jai", 2);
t1.start();
t2.start();
}
}
실행 결과
Adithya : 2 Seats Booking Success Jai : Seats Not Available
정리
실행 결과를 보면 Adithya가 먼저 모니터를 획득해 2석을 성공적으로 예약했고, 남은 좌석이 없기 때문에 Jai는 예약에 실패했습니다. 만약 동기화 블록이 없었다면 두 스레드가 거의 동시에 availableSeats 값을 확인하고 수정하면서, 좌석이 부족한데도 예약이 성공하는 등 잘못된 결과가 나올 수 있습니다.
따라서 여러 스레드가 공유 자원(잔여 좌석, 계좌 잔액, 카운터 변수 등)에 접근하여 읽고 쓰는 경우에는 반드시 synchronized 블록으로 임계 영역(critical section)을 보호해야 합니다. 특히 메서드 전체가 아닌 일부 코드만 동기화가 필요하다면, 동기화 블록을 활용해 잠금 범위를 최소화하는 것이 성능과 안전성을 모두 확보하는 좋은 방법입니다.