[Java] SynchronizedCollections vs ConcurrentCollections[Java] SynchronizedCollections vs ConcurrentCollections

Posted at 2013. 8. 17. 19:04 | Posted in 개발이야기

SynchronizedCollections(동기화된 컬렉션)과 ConcurrentCollections(병렬 컬렉션)



동기화된 컬렉션 클래스는 컬렉션의 내부 변수에 접근하는 통로를 일련화해서 스레드 안전성을 확보했다. 하지만 이렇게 만들다 보니 여러 스레드가 한꺼번에 동기화된 컬렉션을 사용하려고 하면 동시 사용성은 상당 부분 손해를 볼 수 밖에 없다. 하지만 병렬 컬렉션은 여러 스레드에서 동시에 사용할 수 있도록 설계되었다.

ConcurrentMap에는 put-if-absent, replace, condition-remove 등을 정의하고 있다.


기존에 사용하던 동기화 컬렉션 클래스를 병렬 컬렉션으로 교체하는 것만으로도 별다른 위험 요소 없이 전체적인 성능을 상당히 끌어 올릴 수 있다.

<에이콘 - 자바 병렬 프로그래밍(p.137) 발췌>


동기화되지 않은(unsynchronized) 컬렉션

- List: ArrayList, LinkedList

- Map: HashMap

- Set: HashSet

- SortedMap: TreeMap

- SortedSet: TreeSet

- Since JDK 1.2

- 문제점: Thread Safe하지 않다.


동기화된(synchronized) 컬렉션

- Vector, Hashtable, Collections.synchronizedXXX()로 생성된 컬렉션들

- Since JDK 1.2

- 문제점: Thread Safe하나, 두개 이상의 연산을 묶어서 처리해야 할 때 외부에서 동기화 처리를 해줘야 한다. (Iteration, put-if-absent, replace, condition-remove 등)


병렬(concurrent) 컬렉션

- List: CopyOnWriteArrayList

- Map: ConcurrentMap, ConcurrentHashMap

- Set: CopyOnWriteArraySet

- SortedMap: ConcurrentSkipListMap (Since Java 6)

- SortedSet: ConcurrentSkipListSet (Since Java 6)

- Queue 계열:ConcurrentLinkedQueue

- Since Java 5

- 특이사항: Concurrent(병렬/동시성)이란 단어에서 알 수 있듯이 Synchronized 컬렉션과 달리 여러 스레드가 동시에 컬렉션에 접근할 수 있다. ConcurrentHashMap의 경우, lock striping 이라 부르는 세밀한 동기화 기법을 사용하기 때문에 가능하다. 구현 소스를 보면 16개의 락 객체를 배열로 두고 전체 Hash 범위를 1/16로 나누어 락을 담당한다. 최대 16개의 스레드가 경쟁없이 동시에 맵 데이터를 사용할 수 있게 한다. (p.350)

반대로 단점도 있는데, clear()와 같이 전체 데이터를 독점적으로 사용해야할 경우, 단일 락을 사용할 때보다 동기화 시키기도 어렵고 자원도 많이 소모하게 된다. 또한, size(), isEmpty()같은 연산이 최신값을 반환하지 못할 수도 있다. 하지만 내부 상태를 정확하게 알려주지 못한다는 단점이 그다지 문제되는 경우는 거의 없다.


※ Queue, BlockingQueue 인터페이스는 Java 5에서 추가되었다. (Deque, BlockingDeque는 6에서 추가되었다.)

※ Synchronized 컬렉션은 객체 자체에 락을 걸어 독점하게되고, Concurrent 컬렉션은 객체 자체 독점하기가 쉽지 않은 단점이 있지만, 장점이 훨씬 더 많다. Concurrent 컬렉션은 컬렉션 전체를 독점하기 위해서는 충분히 신경을 기울여야 한다.

※ Hash를 기반으로 하는 컬렉션은 hashCode()의 해시값이 넓고 고르게 분포되지 못하면 한쪽으로 쏠린 해시 테이블을 사용하게 되는데, 최악의 경우는 단순한 Linked List와 거의 동일한 상태가 될 수 있다.


//

concurrent 도구 간단 정리concurrent 도구 간단 정리

Posted at 2010. 11. 11. 11:32 | Posted in 개발이야기

POSIX-pthread나 이를 wrapping한 여러 라이브러리들, java concurrent 패키지 등에서 유사한 이름으로 사용되는

concurrent 관련 객체, 메소드 등을 간단히 정리한다.


Mutex: 한번에 하나의 thread만 critical section에 진입 가능 (lock, unlock)

  • lock: 하나의 thread만 lock을 획득할 수 있고, 다른 쓰레드는 lock이 풀릴때 까지 대기.
  • tryLock: lock의 성질에 의해 진입할 수 없는 경우, false등을 반환하여 lock획득에 실패했음을 알리고 즉시 리턴. 
    (아무도 lock을 안잡았으면 잡고, lock이 잡혀있으면 즉시 리턴)
  • timedTryLock: tryLock과 동일하나 lock 획득을 위해 일정시간 대기한다.
  • unlock: lock을 해제
Semaphore: N개의 thread만 critical section에 진입 가능 (acquire, release)
  • acquire: lock과 동일하나 N개의 thread가 진입할 수 있다.
  • tryAcquire: semaphore의 성질에 의해 진입할 수 없는 경우, false등을 반환하여 lock획득에 실패했음을 알리고 즉시 리턴. (semaphore를 획득할 수 있으면 획득, 아니면 즉시 리턴)
  • timedTryAcquire: tryAcquire와 동일하나 semaphore 획득을 위해 일정시간 대기한다.
  • release: acquire로 얻은 semaphore(lock) 반납.
ConditionVariable: 상태 변수 (wait, notify(또는 signal)). Lock 객체와 연계하여 사용됨.
  • wait: 특정 상태가 변경될 때까지 대기함(notify(signal) 또는 notifyAll(broadcast) 이 호출될 때까지 기다림).
  • timedWait: wait와 동일하나 특정 시간 동안만 대기한다.
  • notify(or signal): 대기 상태에 있는(wait를 호출한) thread를 깨운다. 동시에 여러 thread가 대기하고 있는 경우 임의의 thread 하나만 깨어난다.
  • notifyAll(or broadcast): 대기 상태에 있는(wait를 호출한) 모든 thread를 깨운다.

    //