분류 전체보기 1341

Ch11. 동시성 컬렉션 - 동시성 컬렉션

자바 1.5부터 동시성에 대한 많은 혁신이 이루어졌다. 그중에 동시성을 위한 컬렉션도 있다. 여기서 말하는 동시성 컬렉션은 스레드 안전한 컬렉션을 뜻한다.java.util.concurrent 패키지에는 고성능 멀티스레드 환경을 지원하는 다양한 동시성 컬렉션 클래스들을 제공한다. 예를 들어, ConcurrentHashMap , CopyOnWriteArrayList , BlockingQueue 등이 있다. 이 컬렉션들 은 더 정교한 잠금 메커니즘을 사용하여 동시 접근을 효율적으로 처리하며, 필요한 경우 일부 메서드에 대해서만 동기화를 적용하는 등 유연한 동기화 전략을 제공한다. 여기에 다양한 성능 최적화 기법들이 적용되어 있는데, synchronized , Lock ( ReentrantLock ), CAS ..

Ch11. 동시성 컬렉션 - Synchronized

자바가 제공하는 java.util 패키지에 있는 컬렉션 프레임워크들은 대부분 스레드 안전(Thread Safe) 하지 않다. 우리가 일반적으로 사용하는 ArrayList , LinkedList , HashSet , HashMap 등 수많은 기본 자료 구조들은 내 부에서 수많은 연산들이 함께 사용된다. 배열에 데이터를 추가하고 사이즈를 변경하고, 배열을 새로 만들어서 배열의 크기도 늘리고, 노드를 만들어서 링크에 연결하는 등 수많은 복잡한 연산이 함께 사용된다. 그렇다면 처음부터 모든 자료 구조에 synchronized를 사용해서 동기화를 해두면 어떨까? synchronized , Lock , CAS 등 모든 방식은 정도의 차이는 있지만 성능과 트레이드오프가 있다. 결국 동기화를 사용하지 않는 것이 가장 ..

Ch11. 동시성 컬렉션 - 필요한 이유

ArrayList의 Thread Safe 확인여기서는 멀티스레드를 사용하지 않지만, 스레드 1과 스레드 2가 동시에 다음 코드를 실행한다고 가정해 보자.스레드 1: list에 A를 추가한다.스레드 2: list에 B를 추가한다.컬렉션에 데이터를 추가하는 add() 메서드를 생각해 보면, 단순히 컬렉션에 데이터를 하나 추가하는 것뿐이다. 따라서 이것은 마치 연산이 하나만 있는 원자적인 연산처럼 느껴진다. 원자적인 연산은 쪼갤 수 없기 때문에 멀티스레드 상황에 문제가 되지 않는다.물론 멀티스레드는 중간에 스레드의 실행 순서가 변경될 수 있으므로 [A, B] 또는, [B, A]로 데이터의 저장 순서는 변경될 수 있지만, 결과적으로 데이터는 모두 안전하게 저장될 것 같다. 하지만 컬렉션 프레임워크가 제공하는 대..

Ch10. 동기화와 원자적 연산(CAS) - CAS 정리

CAS의 장점낙관적 동기화: 락을 걸지 않고도 값을 안전하게 업데이트할 수 있다. CAS는 충돌이 자주 발생하지 않을 것이라고 가정한다. 이는 충돌이 적은 환경에서 높은 성능을 발휘한다. 락 프리(Lock-Free): CAS는 락을 사용하지 않기 때문에, 락을 획득하기 위해 대기하는 시간이 없다. 따라서 스레드가 블로킹되지 않으며, 병렬 처리가 더 효율적일 수 있다. CAS의 단점충돌이 빈번한 경우: 여러 스레드가 동시에 동일한 변수에 접근하여 업데이트를 시도할 때 충돌이 발생할 수 있다. 충돌이 발생하면 CAS는 루프를 돌며 재시도해야 하며, 이에 따라 CPU 자원을 계속 소모할 수 있다. 반복적인 재시도로 인해 오버헤드가 발생할 수 있다. 스핀락과 유사한 오버헤드: CAS는 충돌 시 반복적인 재시도를..

Ch10. 동기화와 원자적 연산(CAS) - CAS 락 구현

CAS는 단순한 연산뿐만 아니라, 락을 구현하는데 사용할 수도 있다. "synchronized" , "Lock(ReentrantLock)" 없이 CAS를 활용해서 락을 구현해 보자.문제 코드(CAS 적용 전)스레드가 락을 획득하면 lock의 값이 true 가 된다.스레드가 락을 반납하면 lock의 값이 false 가 된다.스레드가 락을 획득하면 while문을 탈출한다.스레드가 락을 획득하지 못하면 락을 획득할 때까지 while문을 계속 반복 실행한다.실행 결과를 보면 기대와는 다르게 "Thread-1", "Thread-2" 둘 다 동시에 락을 획득하고 비즈니스 로직을 동시에 수행해 버린다.실행 결과 분석lock의 초기값은 false이다. "Thread-1"과 "Thread-2"는 동시에 실행된다.Thre..

Ch10. 동기화와 원자적 연산(CAS) - CAS 연산

락 기반 방식의 문제점SyncInteger와 같은 클래스는 데이터를 보호하기 위해 락을 사용한다. 여기서 말하는 락은 synchronized , Lock(ReentrantLock) 등을 사용하는 것을 말한다. 락은 특정 자원을 보호하기 위해 스레드가 해당 자원에 대한 접근하는 것을 제한한다. 락이 걸려 있는 동안 다른 스레드 들은 해당 자원에 접근할 수 없고, 락이 해제될 때까지 대기해야 한다.또한 락 기반 접근에서는 락을 획득하고 해제하는 데 시간이 소요된다. 예를 들어서 락을 사용하는 연산이 있다고 가정하자. 락을 사용하는 방식은 다음과 같이 작동한다.락이 있는지 확인한다. 락을 획득하고 임계 영역에 들어간다. 작업을 수행한다. 락을 반납한다. 여기서 락을 획득하고 반납하는 과정이 계속 반복된다. 1..

Ch10. 동기화와 원자적 연산(CAS) - 원자적 연산

원자적 연산이란?컴퓨터 과학에서 사용하는 원자적 연산(atomic operation)의 의미는 해당 연산이 더 이상 나눌 수 없는 단위로 수행된 다는 것을 의미한다. 즉, 원자적 연산은 중단되지 않고, 다른 연산과 간섭 없이 완전히 실행되거나 전혀 실행되지 않는 성질을 가지고 있다. 쉽게 이야기해서 멀티스레드 상황에서 다른 스레드의 간섭 없이 안전하게 처리되는 연산이라는 뜻이다. 참고: 과거에 원자는 더 이상 나눌 수 없는 가장 작은 단위로 여겨졌다. 그래서 더는 나눌 수 없다는 뜻으로 **원자적 연산**이라는 단어를 사용한다. 물론 현대 물리학에서는 원자가 더 작은 입자들로 구성되어 있다는 것이 밝혀졌다. 하지만 원자적 연산이라는 단어는 그대로 사용한다. 예를 들어서 다음과 같은 필드가 있을 때, (v..

Ch09. 생산자 소비자 문제 - BlockingQueue

BlockingQueueBoundedQueue를 스레드 관점에서 보면 큐가 특정 조건이 만족될 때까지 스레드의 작업을 차단(blocking)한다.데이터 추가 차단: 큐가 가득 차면 데이터 추가 작업( put() )을 시도하는 스레드는 공간이 생길 때까지 차단된다.데이터 획득 차단: 큐가 비어 있으면 획득 작업( take() )을 시도하는 스레드는 큐에 데이터가 들어올 때까지 차단된다.그래서 스레드 관점에서 이 큐에 이름을 지어보면 BlockingQueue라는 이름이 적절하다. 자바는 생산자 소비자 문제, 또는 한정된 버퍼라고 불리는 문제를 해결하기 위해 java.util.concurrent.BlockingQueue라는 인터페이스와 구현체들을 제공한다.package java.util.concurrent;p..

Ch09. 생산자 소비자 문제 - 스레드의 대기

synchronized 대기대기 1: 락 획득 대기"BLOCKED" 상태로 락 획득 대기synchronized를 시작할 때 락이 없으면 대기다른 스레드가 synchronized를 빠져나갈 때 대기가 풀리며 락 획득 시도 대기 2: wait() 대기"WAITING" 상대로 대기wait()를 호출했을 때 스레드 대기 집합에서 대기다른 스레드가 notify()를 호출했을 때 빠져나감소비자 스레드: c1 , c2 , c3생산자 스레드: p1 , p2 , p3소비자 스레드 c1 , c2 , c3 가 동시에 실행된다고 가정하자.소비자 스레드 c1 이 가장 먼저 락을 획득한다.c2 , c3는 락 획득을 대기하며 "BLOCKED" 상태가 된다.c2 , c3는 락 획득을 시도하지만, 모니터 락이 없기 때문에 락을 대기하..

Ch09. 생산자 소비자 문제 - 생산자/소비자 대기 공간 분리

lock.newCondition()을 두 번 호출해서 ReentrantLock을 사용하는 스레드 대기 공간을 2개 만들었다.Condition 분리consumerCond : 생산자를 위한 스레드 대기 공간producerCond : 소비자를 위한 스레드 대기 공간이렇게 하면 생산자 스레드, 소비자 스레드를 정확하게 나누어 관리하고 깨울 수 있다.put(data) - 생산자 스레드가 호출큐가 가득 찬 경우: producerCond.await()를 호출해서 생산자 스레드를 생산자 전용 스레드 대기 공간에 보관한다.데이터를 저장한 경우: 생산자가 데이터를 생산하면 큐에 데이터가 추가된다. 따라서 소비자를 깨우는 것이 좋다. consumerCond.signal()를 호출해서 소비자 전용 스레드 대기 공간에 신호를 ..