본문 바로가기

Programming/Java

[Java] synchronized 메소드와 일반 메소드로 같은 자원에 접근하면 thread-safe할까?

면접 때 받았던 질문을 바탕으로 작성한 글입니다.

 

synchronized가 완전히? 걱정없는? Thread-safe한 환경을 구성해줄 것이라고 믿고 있었지만 그렇지도 않았다.

 

synchronized 메소드들만 사용 → thread-safe

package test;

public class synTest {
	private int a = 0;
	
	public static void main(String[] args) {
		synTest syn = new synTest();

		
		new Thread( () -> {
			syn.setter();
		}).start();
		
		new Thread( () -> {
			for(int i = 0 ; i < 100 ; i++)
				System.out.println(syn.getter());
		}).start();
		
	}

	public synchronized void setter() {
		for(int i = 0 ; i < 1000000 ; i++)
			a++;
	}
	
	public synchronized int getter() {
		return a;
	}
}

결과

1000000
1000000
...(중략)...
1000000
1000000

먼저 호출한 Thread인 setter()를 실행하는 스레드부터 실행이 완료될 때까지 자연스레 두번째 Thread는 실행되지 않는다.

즉, synchronized 메소드끼리는 완전히 thread-safe하게 잘 작동한다.

 

synchronized 메소드와 일반 메소드를 사용할 경우 → non-thread-safe

아래와 같은 상황에서는 thread-safe하지 않다.

package test;

public class synTest2 {
	private int a = 0;
	
	public static void main(String[] args) {
		synTest2 syn = new synTest2();

		
		new Thread( () -> {
			syn.setter();
		}).start();
		
		new Thread( () -> {
			for(int i = 0 ; i < 100 ; i++)
				System.out.println(syn.getter());
		}).start();
		
	}

	public synchronized void setter() {
		for(int i = 0 ; i < 1000000 ; i++)
			a++;
	}
	
	public int getter() {
		return a;
	}
}

 

이 코드의 실행 결과는 아래와 같다.

34972
46173
48362
50178
...(중략)... 
662635
666794
677462
693259
717501
726030
730935 

 

synchronized가 없는 메소드는 다른 synchronized 메소드가 사용 중인 자원에 그냥 접근 가능하다는 것

 

나만 그런건진 모르겠지만, 난 당연히 synchronized가 사용하는 자원들은 모든 메소드에서 접근 불가능하게 되는 줄 알았다.. 그게 아니었다.

실제로 Thread-safe인 대표적인 클래스인 StringBuffer도 모든 메소드에 synchronized가 붙어있는데, 괜히 그런 게 아니었다.

아니면 다른 메소드 실행 중간에 수정중인 값을 조회할 수 있으니까..

심지어 저 두번째 Thread가 값을 조회만 해서 다행이지, 수정하는 메소드였다면 완전 엉킨다.

 

 

면접 때 질문을 받고 틀려서(...) 처음으로 깨달았던 건데, 알려주셔서 매우 감사하다.

앞으로 주의해서 사용해야겠다.