프록시 (Proxy)
- Proxy : 대리자, 대신 처리해주는 역할을 수행한다.
- 프록시는 원본과 똑같이 생겼고 원본과 같은 인터페이스를 구현함으로 호출하는 메소드도 똑같다.
- 프록시는 내부에 원본을 가지고 있으며 프록시가 필요한 일의 일부를 처리하고 그 다음 원본을 호출하는 구조를 만들 수 있다.
//인터페이스, 추상화 역할
public interface SimpleList {
void add(Object e);
}
//SimpleList 구현체
public class BasicList implements SimpleList {
void add(Object e) {...}
}
//SimpleList 구현체이자 프록시
public class SyncProxyList implements SimpleList {
private SimpleList target;
public SyncProxyList(SimpleList target) {
this.target = target;
}
synchronized void add(Object e) {
target.add(e);
}
}
- 예제는 SimpleList라는 인터페이스와 구현체 BasicList와 프록시 역할을 하는 SyncProxyList가 있다.
- 코드에는 추상화에 의존하도록 코드를 작성하면 클라이언트 입장에서는 SimpleList를 구현한 어떤 것이 와도 상관없다.
- 일반적인 구현체와 프록시는 같은 인터페이스를 구현했기에 클라이언트 입장에서 보면 원본과 똑같이 생겼고 호출할 메소드도 같다.
- 여기서 프록시는 일반 구현체에 synchronized 동기화를 적용한다.
- 원본은 Thread UnSafe 하지만 프록시가 동기화를 적용하고 원본을 호출하기 때문에 원본도 동기화가 적용된 상태로 호출된다.
- 원본 코드인 BasicList를 수정하지 않고 프록시인 SyncProxyList를 통해 동기화 기능을 적용했다.
- 프록시인 SyncProxyList 하나로 SimpleList 인터페이스의 모든 구현체를 동기화 할 수 있다.
같은 인터페이스를 구현한 구현체는 같은 구조를 갖기 때문에
- 프록시인 SyncProxyList 하나로 SimpleList 인터페이스의 모든 구현체를 동기화 할 수 있다.
- 예제와 같이 구현한 코드가 프록시 패턴이다.
프록시 패턴(Proxy Pattern) : 객체지향 디자인 패턴 중 하나로 어떤 객체에 대한 접근을 제어하기 위해서 그 객체의 대리인 또는 인터페이스 역할을 하는 객체를 제공하는 패턴이다.
실제 객체에 대한 참조를 유지하면서, 그 객체에 접근하거나 행동을 수행하기 전에 추가적인 처리를 할 수 있도록 한다.
- 프록시 패턴의 주요 목적
- 접근 제어 - 실제 객체에 대한 접근을 제한하거나 통제할 수 있다.
- 성능 향상 - 실제 객체의 생성을 지연시키거나 캐싱하여 성능을 최적화할 수 있다.
- 부가 기능 제공 - 실제 객체에 추가적인 기능(로깅, 인증, 동기화등)을 투명하게 제공할 수 있다.
동시성 컬렉션
- Thread Safe : 여러 스레드가 동시에 접근해도 괜찮은 경우를 의미한다.
- 컬렉션 프레임워크 대부분은 Thread UnSafe하다.
- 컬렉션 기본적인 것들을 synchronized 동기화 기법을 사용하게 설계하면 성능이 느려진다.
- 이전에 사용했던 컬렉션 중 Vector는 ArrayList와 같은 기능을 제공하지만 모든 메소드에 synchronized를 통한 동기화가 되어 있어서 단일 스레드 환경에서 성능 저하가 발생하기에 사용을 권장하지 않는다.
- synchronized 프록시 방식
List<String> list = Collections.synchronizedList(new ArrayList<>());
- Collections.synchronizedXxx()는 SynchronizedRandomAccessXxx 객체를 반환한다.
- SynchronizedRandomAccessXxx는 기본 인터페이스 List, Map, Set 등에 synchronized를 추가하는 프록시 역할을 한다.
- Collections가 제공하는 동기화 프록시 기능 덕에 Thread UnSafe한 수 많은 컬렉션들을 매우 편리하게 Thread Safe한 컬렉션으로 변경해서 사용할 수 있다.
- synchronized 프록시 방식의 단점
- 동기화 오버헤드가 발생한다.
- 전체 컬렉션에 대한 동기화가 이루어지기에 동기화를 부분 블록만 할 수 있는 것이 아닌 전체를 함으로 최적화가 이루어 질 수 없기에 잠금 범위가 넓어지고 효율성을 저하시키는 과도한 동기화가 이루어진다.
- synchronized 프록시 방식의 단점을 보완하기 위해서 java.util.concurrent 패키지에 동시성 컬렉션(concurrent collection)을 제공한다.
- 동시성 컬렉션 : Thread Safe한 컬렉션을 의미하며 java.util.concurrent 패키지에서 제공하며 더 정교한 잠금 매커니즘을 사용해 동시 접근을 효율적으로 처리하고 일부 메소드에 대해서만 동기화를 적용하는 등 유연한 동기화 전략을 제공한다.
- synchronized, ReentrantLock, CAS 등등 다양한 방법을 섞어서 동기화를 구현함과 동시에 성능도 최적화 했다.
- 동시성 컬렉션의 종류
- List - CopyOnWriteArrayList (ArrayList 대안)
- Set - CopyOnWriteArraySet (HashSet 대안), ConcurrentSkipListSet (TreeSet 대안)
- Map - ConcurrentHashMap (HashMap 대안), ConcurrentSkipListMap (TreeMap 대안)
- Queue - ConcurrentLinkedQueue : 동시성 큐, 비 차단(non-blocking) 큐이다.
- Deque - ConcurrentLinkedDeque : 동시성 데크, 비 차단 큐이다.
- 실무에서 ConcurrentHashMap은 종종 사용된다.
- 멀티스레드 환경에서 입력 순서를 유지하는 구현체는 제공되지 않기에 synchronized 프록시 방식인 Collections.synchronizedXxx()를 사용해야한다.
- 스레드를 차단하는 블로킹 큐
- BlockingQueue
- ArrayBlockingQueue - 크기가 고정된 블로킹 큐로 공정 모드를 사용할 수 있다.
- LinkedBlockingQueue - 크기가 무한하거나 고정된 블로킹 큐이다.
- PriorityBlockingQueue - 우선순위가 높은 요소를 먼저 처리하는 블로킹 큐이다.
- SynchronousQueue - 데이터를 저장하지 않는 블로킹 큐로 생산자가 데이터를 추가하면 소비자가 데이터를 받을 때까지 대기한다. (직접적인 핸드오프 매커니즘 제공)
- DelayQueue - 지연된 요소를 처리하는 블로킹 큐로 지정된 지연 시간이 지난 후에 요소가 소비될 수 있다.
- BlockingQueue
출처 : [인프런 김영한 실전 자바 - 고급편]
김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런
김영한 | 멀티스레드와 동시성을 기초부터 실무 레벨까지 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다?
www.inflearn.com
'Java > [인프런 김영한 실전 자바 - 고급편]' 카테고리의 다른 글
[인프런 김영한 실전 자바 - 고급편] 스레드 풀과 Executor 프레임워크 1, 2 (1) | 2024.08.08 |
---|---|
[인프런 김영한 실전 자바 - 고급편] CAS - 동기화와 원자적 연산 (0) | 2024.08.06 |
[인프런 김영한 실전 자바 - 고급편] 생산자 소비자 문제 1, 2 (0) | 2024.08.06 |
[인프런 김영한 실전 자바 - 고급편] concurrent.Lock (0) | 2024.08.02 |
[인프런 김영한 실전 자바 - 고급편] 동기화 - synchronized (0) | 2024.08.02 |