Java

[Java] 혼동되는 synchronized 동기화 정리

제리 . 2021. 5. 28. 18:30

synchronized는 lock을 사용해 동기화를 시킨다. 하지만 사용 방식에 따라 혼동되기 쉽다. synchronized는 4가지의 사용법이 있다. sychronized method, sychronized block, static sychronized method, static synchonized block. 이 포스팅에서는 이 4가지 방식의 차이인 lock이 적용되는 범위를 중점으로 다룬다.  

 

1. synchronized method

 

synchronized method는 클래스의 인스턴스에 대하여 lock을 건다.  다음과 같은 상황을 보자.

 

첫 번째로 하나의 인스턴스에 대하여 2개의 thread가 경합하는 상황이다.

결과를 확인해보자.

순서대로 lock을 획득하고 반납하였다.

 

두 번째로 각각의 인스턴스를 만들고 메서드를 실행시켜보자.

결과를 확인해보자

이 상황에서는 lock을 공유하지 않기 때문에 동기화가 발생하지 않는다.

 

실험 결과를 보면 알 수 있듯이 synchronized method는 인스턴스에 대하여 lock을 건다. 인스턴스에 대해서 lock을 건다고는 표현이 인스턴스 접근 자체가 lock이 걸리는 것인지 혼란스러울 수 있는 데 그렇지 않다. 다른 예시를 보자.

여기서 synchronized가 적용되지 않은 print()메서드를 추가하고 lock이 걸린 중간에 호출해봤다.

결과는 run()메서드의 lock과 상관없이 정상적으로 호출된다.

 

만약 print()메서드도 synchronized가 적용되어있다면 어떻게 될까?

이번에는 동기화가 발생했다.  

 

결과를 정리해보자면 synchronize메서드는 인스턴스 단위로 lock을 건다. 이때, synchronized가 적용된 모든 object에 대해서 lock을 공유한다.

 

2. static synchronized method

static이 포함된 synchronized method방식은 우리가 일반적으로 생각하는 static의 성질을 갖는다. 인스턴스가 아닌 클래스 단위로 lock이 발생한다.

 

결과는 아래와 같다.

다른 인스턴스이지만 클래스 단위로 lock이 발생했다. 만약, static synchronized method와 synchronized method가 섞여있다면 어떨까?

 

결과는 아래와 같다.

인스턴스 단위의 lock과 클래스 단위의 lock은 공유되지 않았다.

 

static synchronized method를 정리해보면 클래스 단위로 lock을 걸지만, 인스턴스 단위의 synchronized method와 lock을 공유하지 않는다.

 

3. synchronized block

synchronized block은 인스턴스의 block단위로 lock을 건다. 이때, lock객체를 지정해줘야한다.

 

이렇게 block을 지정해준다. 이때 this는 A객체를 의미하고 block이 method전체에 적용되어있기 때문에 method단위로 lock을 거는 것과 같다.

 

하지만 위와 같이 여러 로직이 섞여있는 사이에 필요한 부분만 lock을 걸 수 있다. lock은 synchronized block에 진입할 때 획득하고 빠져나오면서 반납하므로 block으로 범위를 지정하는게 더 효율적이다. 

 

synchronized block도 method와 동일하게 인스턴스에 대해서 적용된다. 따라서 다음과 같은 상황에서 lock은 각각의 인스턴스별로 관리된다.

 

 

synchronized block을 사용하는 다른 방식이다.

 

이전보다 약간 복잡해졌다. lock 객체를 A클래스가 아닌 B클래스의 인스턴스 b로 사용하고 있다.

thread1, thread2는 b를 사용하는 method를 호출하고 있지만 thread3는 b와 상관없는 method이기 때문에 b의 lock과 상관없이 출력되었다.

 

이번에는 lock 객체를 인스턴스가 아닌 class로 사용해보자.

 

block에 .class형식을 사용하면 인스턴스가 아닌 class단위로 lock을 건다.

 

정리하자면, synchronized block은 lock을 거는 객체를 지정할 수 있다. 객체를 넘기면 인스턴스 단위로 lock을 걸고 .class형식으로 넘기면 클래스 단위의 lock을 건다. 

 

4. static synchronized block

static method안에 synchronized block을 지정할 수 있다. static의 특성상 this같이 현재 객체를 가르키는 표현을 사용할 수 없다. static synchroinzed method방식과 차이는 lock객체를 지정하고 block으로 범위를 한정지을 수 있다는 점이다. 이외에 클래스 단위로 lock을 공유한다는 점은 같다.

 

 

 

+ 동기화 순서

synchronized는 thread별 동기화 순서를 보장할까?

순서를 보장하지 않는다.

 

 

결론

synchronized method

인스턴스 단위로 lock이 걸린다.

메서드가 시작될 때부터 종료될 때까지 동기화가 발생한다.

동일 인스턴스내에서 synchronized키워드가 적용된 곳에서는 lock을 공유한다. 

 

synchronized block

인스턴스 단위로 lock이 걸린다.

block내부에서 동기화가 발생한다.

lock 객체를 지정하여 특정 대상에만 lock을 걸 수 있다.

lock을 객체로 설정하면 해당 인스턴스만 lock이 걸리고 .class형식으로 설정하면 클래스 단위로 lock을 건다.

 

static synchronized method

클래스 단위로 lock이 걸린다.

메서드가 시작될 때부터 종료될 때까지 동기화가 발생한다.

static synchronized와 synchronized가 혼용되어있을 때 각자의 lock으로 관리된다.

 

static synchronized block

클래스 단위로 lock이 걸린다.

block내부에서 동기화가 발생한다.

lock객체를 지정하여 특정 대상에만 lock을 걸 수 있다.

lock을 객체로 설정하면 해당 인스턴스만 lock이 걸리고 .class형식으로 설정하면 클래스 단위로 lock을 건다.

 

 

+ 인스턴스 단위라는 것은 synchronized 키워드가 적용된 곳에서는 전부 lock을 공유해서 쓴다. synchronized와 무관한 곳은 lock과 상관없다. 클래스 단위도 마찬가지로 synchronized 키워드가 적용된 곳에서 해당된다. 

 

+ method와 block의 차이는 method는 해당 객체 전체에 lock을 걸고, block은 lock의 대상을 지정할 수 있으며 block으로 동기화가 적용되는 범위를 한정시킬 수 있다.

 

+ synchronized는 thread의 동기화 순서를 보장하지 않는다.