프로그래밍백엔드 개발자

Java에서 synchronized 블록이 어떻게 작동하는지 설명하세요. 그 특징은 무엇이며, 동기화할 객체를 선택하는 방법과 잘못된 선택이 어떻게 오류를 발생시킬 수 있는지 설명하세요.

Hintsage AI 어시스턴트로 면접 통과

답변.

synchronized는 코드의 중요한 부분에 대해 스레드 안전한 접근을 수행할 수 있게 해주는 키워드입니다. 이는 메서드(예: public synchronized void foo())나 코드 블록(synchronized(obj) { ... })에 사용할 수 있습니다. 스레드가 synchronized 블록에 들어가면, 객체의 모니터(락)를 얻게 됩니다. 모니터가 사용 중인 동안, 다른 스레드는 동일한 객체를 모니터로 사용하는 다른 synchronized 블록에 들어올 수 없습니다.

특징:

  • 동일한 객체에 대한 동기화는 상호 배제를 보장합니다(동시에 하나의 스레드만 블록을 실행할 수 있음).
  • this, 정적 객체(예: 클래스) 또는 임의의 객체에 대해 동기화할 수 있습니다.
  • 여러 스레드에서 접근 가능한 객체나 지나치게 로컬한 객체를 모니터로 선택하면 스레드 안전성을 침해할 수 있습니다.

동기화 객체 선택의 예

public class Counter { private int count; private final Object lock = new Object(); // 프라이빗 객체 public void increment() { synchronized(lock) { count++; } } }

프라이빗 락을 사용하는 것이 더 나은 이유: 퍼블릭 객체(예: this 또는 공개 문자열)에 대해 동기화하면 외부 코드가 모니터를 획득할 수 있어 데드락이나 잘못된 동작을 초래할 수 있습니다.

함정 질문.

질문: 고정된 값을 가진 String 객체에 대해 동기화하면 어떻게 될까요?

답변: Java의 문자열은 내부적으로 관리됩니다(같은 리터럴에 대해 동일한 객체 사용). synchronized("lock") 형태의 문자열에 대해 동기화하면, 같은 리터럴에 대해 동기화하는 다른 코드와 우연히 충돌할 수 있어 프로그램의 완전히 다른 부분 간에 예기치 않은 블록킹이 발생할 수 있습니다.

예시 (이렇게 하면 안 됩니다):

synchronized("LOCK") { ... }

주제의 미세한 차이를 모를 경우 발생하는 실제 오류 사례.


이야기

멀티스레드 거래 시스템에서 동기화를 위해 퍼블릭 객체를 사용했으며, 외부 코드가 락을 획득하여 서로 다른 모듈의 스레드 간에 임시 데드락이 발생하고 거래소에서의 대기가 발생했습니다.


이야기

젊은 개발자가 문자열 리터럴에 대해 컬렉션 접근을 동기화했습니다. 코드의 다른 부분도 같은 값의 문자열에 대해 동기화되어 이러한 스레드가 서로 대기하게 되어 비즈니스 로직이 급격히 느려졌습니다.


이야기

매번 새로운 객체를 선택하여 동기화했습니다: synchronized(new Object()) { ... }. 그 결과, 동기화가 전혀 작동하지 않았고, 데이터에 여러 스레드가 동시에 접근할 수 있었습니다. 이는 부하 테스트에서만 발견되었습니다.