본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 스레드 안전을 문서화 하자.

by 돼지왕 왕돼지 2017. 3. 14.
반응형

 [Effective Java] 스레드 안전을 문서화 하자.


annotation, API, collections.synchronized, ConcurrentHashMap, conditionally thread-safe, dnial of service attack, dos attack, Effective JAVA, enum, Final, immutable, javadoc, not thread-safe, notthreadsafe, Operation, private lock, private lock object, private 락 객체, random, static, synchronized, thread hostile, thread safe, thread-safe, threadsafe, unconditionally thread-safe, Wrapper, [Effective Java] 스레드 안전을 문서화 하자., 가변, 가정, 고의, 과도한 동기화, 동기화, 동기화된 메소드, 동시적 사용, 드물지만, 락, 메소드, 메소드 호출, 무조건적 스레드 안전, 문서화, 베이스 클래스, 변경자, 부주의한 내용 변경, 불변, 불변성, 불충분한 동기화, 비용, 상세 구현, 상속, 상속을 위해 설계된 클래스, 서브 클래스, 서비스 거부 공격, 순서, 스레드 안전, 스레드 안전하지 않음, 스레드 적대, 신중, 심각한 에러, 안전, 안전한 동시적 사용, 오퍼레이션, 외부 동기화, 외부 제공, 유연성, 자신의 인스턴스 락, 전역, 전역적, 접근, 접근 가능한 락, 조건적 스레드 안전, 주의, 충분, 클라이언트, 클래스


-
클래스 행동을 문서화하지 않으면, 프로그래머는 가정에 의존해서 그 클래스를 사용해야 한다.
만일 그런 가정들이 잘못되면, 그로 인한 프로그램은 불충분한 동기화나 과도한 동기화를 하게 될 것이다.
어떤 경우든, 심각한 에러가 유발될 수 있다.


-
메소드 선언부의 synchronized 변경자는 메소드의 상세 구현 부분이지 외부로 제공되는 API 가 아니다.
즉 Javadoc 에 synchronized 가 공개되지 않는다.
synchronized 변경자가 있다는 것이 스레드 안전을 문서화하기에 충분한 것은 아니다.
동시적 사용을 안전하게 하려면, 해당 클래스가 어떤 수준의 스레드 안전을 지원하는지 명확하게 문서화해야 한다.


-
다음은 스레드 안전 수준을 요약한 것이다.

불변(immutable)
    이 클래스의 인스턴스는 상수를 나타내므로 별도의 외부 동기화가 필요 없다.

무조건적 스레드 안전(unconditionally thread-safe)
    인스턴스는 가변적.
    그러나 이 클래스의 인스턴스가 외부 동기화 없이 동시적으로 사용될 수 있을 만큼 내부 동기화가 충분하다.
    Random, ConcurrentHashMap 등이 그 예

조건적 스레드 안전(conditionally thread-safe)
    무조건적 스레드 안전과 같으나, 안전한 동시적 사용을 위해 일부 메소드에서 외부 동기화를 필요로 한다.
    외부 동기화를 필요로 하는 Collections.synchronized wrapper 메소드들이 반환하는 컬렉션들이 여기에 해당한다.

스레드 안전하지 않음(not thread-safe)
    동시적으로 사용하려면, 클라이언트에서 메소드 호출시 자신이 선택한 방법으로 외부 동기화해줘야 한다.

스레드 적대(thread hostile)
    모든 메소드 호출 시 외부 동기화를 하더라도 이 클래스는 동시적 사용에 안전하지 않다.
    동기화하지 않고 static 데이터를 변경하면 스레드 적대가 초래된다.


위의 안전 수준은 자바 동시성의 스레드 안전 annotations 의
불변(Immutable), 스레드 안전(ThreadSafe), 스레드 안전하지 않음(NotThreadSafe)와 대응된다.


-
조건적 스레드 안전에 해당하는 클래스를 문서화할 때는 주의가 필요하다.
메소드 호출을 어떤 순서로 할 때 외부 동기화가 필요하고, 그런 순서로 메소드를 실행할 때 어떤 락(드물지만)을 획득해야 하는지를 나타낸다.


-
enum 타입의 불변성은 문서화할 필요 없다.


-
전역적으로 접근 가능한 락을 사용하는 클래스에서는, 클라이언트가 순차적인 메소드 호출을 자동으로 실행할 수 있게 해 준다.
그러나 이런 유연성에는 비용이 따른다.
실수나 고의적으로 일어날 수 있는 일인데, 클라이언트가 전역적으로 접근 가능한 락을 오랜 기간 동안 잡고 있으면서 서비스 거부 공격(denial-of-service attack을 감행할 수 있다.
이런 경우 private 락 객체(private lock object)를 사용해야 한다.
private lock 객체는 final 로 만들어 부주의한 내용 변경을 막는 것이 좋다.
private lock 객체는 상속을 위해 설계된 클래스에 특히 잘 맞는다.
자신의 인스턴스를 락 객체로 사용하는 경우 서브 클래스에서 베이스 클래스의 오퍼레이션을 실수로 쉽게 방해할 수 있다. 그 반대도 마찬가지.


-
조건적 스레드 안전 클래스는 private 락 객체를 사용하기 어렵다.
특정 메소드 호출을 수행할 때 클라이언트가 어떤 락을 획득해야하는지 문서화해야 하기 때문이다.


 Summary


모든 클래스는 자신의 스레드 안전 속성을 명확하게 문서화해야 한다.
내용을 작성할 떄는 신중하게 문장을 기술하거나 쓰레드 안전 annotation 을 사용한다.
synchronized 변경자는 문서에 나타내지 않는다.
조건적 스레드 안전에 해당하는 클래스에서는 메소드 호출을 어떤 순서로 할 때 외부 동기화가 필요하고, 그런 순서로 메소드를 실행할 때 어떤 락을 획득해야 하는지를 문서화해야 한다.
무조건적 스레드 안전에 해당하는 클래스를 작성한다면, 동기화된 메소드 대신 private 락 객체의 사용을 고려하자.
그렇게 하면, 클라이언트와 서브 클래스가 동기화를 방해하는 것을 막을 수 있으며 동시성을 제어하는 더욱 복잡한 방법을 향후 배포판에서 채택할 수 있는 유연성을 제공한다.





반응형

댓글