병렬적으로 사용해도 안전한 클래스가 되려면, 어떤 수준의 스레드 안전성을 제공하는 클래스인지 문서에 명확히 남겨야 한다.
변경 불가능 : 이 클래스로 만든 객체들은 상수다. 따라서 외부적인 동기화 메커니즘 없이도 병렬적으로 이용 가능하다.
· String, Long, BigInteger,..
무조건적 스레드 안전성 : 이 클래스의 객체들은 변경이 가능하지만 적절한 내부 동기화 메커니즘을 갖추고 있어서 외부적으로 동기화 메커니즘을 적용하지 않아도 병렬적으로 사용할 수 있다.
· Random, ConcurrentHashMap..
조건부 스레드 안전성 : 무조건적 스레드 안전성과 거의 같은 수준이나 몇몇 스레드는 외부적 동기화가 없이는 병렬적으로 사용할 수 없다. 아래와 같은 객체 반복자는 외부적 동기화 없이는 병렬적으로 사용할 수 없다.
· Collections.synchronized 계열메서드가 반환하는 포장 객체들..
스레드 안전성 없음 : 이 클래스의 객체들은 변경 가능하다. 해당 객체들은 병렬적으로 사용하려면 클라이언트는 메서드를 호출하는 부분을 클라이언트가 선택한 외부적 동기화 수다능로 감싸야 한다.
· ArrayList나 HashMap같은 컬렉션 구현체
다중 스레드에 적대적 : 설사 메서드를 호출하는 모든 부분을 외부적 동기화 수단으로 감싸더라도 안전하지 않다. 동기화 없이 정적 데이터를 변경하기 때문이다. 누구도 고의로 이러하게 만들지 않고 개발의 실수이다.
· System.runFinalizersOnExit가 스레드에 적대적이며, 현재는 폐기 되었다.
조건부 스레드 안전성 클래스에 대한 문서를 만들 때는 신중해야 한다.
· 어떤 순서로 메서드 호출할 때 외부 동기화 메커니즘을 동원해야하는지.
· 그 순서로 메서드를 실행하려면 어떤 락을 사용해야 하는지.
· 다른 객체에 대한 뷰 역할을 하는 객체일 경우 원래 객체에 대해 동기화를 해야한다.
Map<K, V> m = Collections.syncronizedMap(new HashMap<K, V>());
Set<K> s = m.KeySet(); //동기화 블록 안에 있을 필요 없다.
syncronized(m){ //s가 아니라 m에 대해 동기화
for(K key : s){
key.f();
}
· enum같은 변경 불가능 클래스에는 변경 불가능성을 문서에 남길 필요가 없다.
· 반환값 자료형을 보고 명확하게 알 수 있는경우를 빼고 정적 팩토리 안에서 자기가 반환하는 객체에 스레드 안전성을 문서에 남겨야 한다.
// Dos 공격을 피하기 위한 private 객체 락
private final Object lock = new Object();
public void foo(){
synchronized(lock){
...
}
}
결론
· 스레드 안전성 어노테이션을 이용하건 간에 모든 클래스는 자신의 스레드 안전성 수준을 문서로 분명히 밝혀야 한다.
· synchronized키워드는 문서에 아무런 역할을 하지 못한다.
· 조건부 스레드 안전성을 제공하는 클래스는 어떤 순서로 메서드를 호출할 때 외부적 동기화가 필요한 문서에 밝혀야하고 그때 어떤 락을 획득하게 되는지도 밝혀야한다.
· 무조건적 스레드 안전성을 제공하는 클래스를 구현할때는 메서드를 synchronized로 선언하는 대신 private 락 객체를 이용하면 어떨지 따져보라
'IT > Programming' 카테고리의 다른 글
<Effective Java> RULE 68 스레드보다는 실행자와 태스크를 이용하라 (0) | 2023.04.26 |
---|---|
<Effective Java> RULE 69 wait이나 notify대신 병행성 유틸리티를 이용하라 (0) | 2023.04.26 |
<Effective Java> RULE 71 초기화 지연은 신중하게 하라 (0) | 2023.04.25 |
<Effective Java> RULE 72 스레드 스케줄러에 의존하지 마라 (0) | 2023.04.25 |
<Effective Java> RULE 73 스레드 그룹은 피하라 (0) | 2023.04.25 |