면접 때 받았던 질문을 바탕으로 작성한 글입니다.
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가 값을 조회만 해서 다행이지, 수정하는 메소드였다면 완전 엉킨다.
면접 때 질문을 받고 틀려서(...) 처음으로 깨달았던 건데, 알려주셔서 매우 감사하다.
앞으로 주의해서 사용해야겠다.
'Programming > Java' 카테고리의 다른 글
junit 예시 (get, post, patch, delete, exchange, assert) (0) | 2020.03.01 |
---|---|
[Spring] JSON 형식으로 request, response하기 (0) | 2020.01.12 |
[Java] Hashtable VS HashMap VS ConcurrentHashMap (0) | 2019.11.12 |
[Java] Garbage Collection (GC) 알고리즘 (1) | 2019.11.11 |
JDK vs JRE vs JVM (0) | 2019.11.05 |