[Java] Vector
정의
java.util.Vector 는 ArrayList 와 거의 동일한 동적 배열 기반 List 이지만, 모든 public 메서드가 synchronized 로 묶여 있다. Java 1.0 (1996) 부터 존재하는 레거시 컬렉션.
JDK 1.2 의 Collections Framework 도입 시 List 인터페이스에 retrofit 됐으며, 같은 인터페이스를 구현하는 ArrayList 가 등장했다. 새 코드에서는 거의 쓰지 않는다.
시각화
내부 구조
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement; // 0 이면 두 배씩 증가 (ArrayList 와 다름)
public synchronized boolean add(E e) { ... }
public synchronized E get(int index) { ... }
public synchronized E remove(int index) { ... }
// ... 거의 모든 메서드에 synchronized
}
ArrayList 와의 두 가지 차이:
capacityIncrement: 0 일 때 두 배씩 확장 (ArrayList 는 1.5 배).- 모든 메서드가
synchronized: 메서드 호출마다this의 모니터 락을 획득.
주요 연산 비용
복잡도 자체는 ArrayList 와 동일. 다만 매 호출마다 락 acquire/release 가 더해진다.
| 메서드 | 시간 | 추가 비용 |
|---|---|---|
get(int i) | O(1) | 락 acquire/release |
add(E e) | amortized O(1) | 락 acquire/release |
add(int i, E e) | O(n) | 락 acquire/release |
remove(int i) | O(n) | 락 acquire/release |
size() | O(1) | 락 acquire/release |
단일 스레드 환경이라면 락은 무경합 (uncontended) 이라 비용이 매우 작지만, 그래도 ArrayList 대비 측정 가능한 오버헤드가 있다.
메서드별 동기화의 한계
각 메서드 호출은 atomic 하지만, 복합 연산 은 race condition 의 여지가 그대로 남는다.
Vector<Integer> v = new Vector<>();
// ...
// ❌ 각 메서드는 atomic 이지만 두 호출 사이에 다른 스레드가 끼어들 수 있다
if (!v.contains(42)) {
v.add(42); // 다른 스레드가 그 사이에 42 를 넣었을 수 있음
}
// ✅ 명시적 락으로 묶기
synchronized (v) {
if (!v.contains(42)) {
v.add(42);
}
}
CAUTION
“Vector 가 thread-safe 하다” 는 오해다. 단일 메서드는 thread-safe 지만, 두 개 이상의 메서드를 묶는 일관성은 호출자가 따로 보장해야 한다. 진정한 동시성 컬렉션은 CopyOnWriteArrayList 또는 ConcurrentHashMap 같은 java.util.concurrent 의 컬렉션.
iterator 의 fail-fast
Vector 의 iterator 도 fail-fast iterator. 순회 도중 다른 스레드가 add/remove 하면 ConcurrentModificationException. 락으로 외부 동기화를 해야 안전한 순회가 가능하다.
synchronized (v) {
for (Integer x : v) {
process(x); // 이 블록 안에서는 다른 스레드 진입 불가
}
}
ArrayList vs Vector
| 항목 | ArrayList | Vector |
|---|---|---|
| 동기화 | ✗ | ✓ (메서드 단위) |
| 도입 시점 | JDK 1.2 (1998) | JDK 1.0 (1996) |
| 용량 증가 | 1.5 배 | 2 배 (또는 capacityIncrement) |
| 메서드 호출 비용 | 작음 | + 락 비용 |
| 복합 연산 안전성 | 외부 동기화 필요 | 여전히 외부 동기화 필요 |
| 새 코드 권장 | ✓ | ✗ |
왜 새 코드에서 쓰지 않는가
- 단일 스레드 라면 락이 불필요한데도 비용을 낸다.
- 다중 스레드 라도 메서드별 락만으로는 복합 연산이 안전하지 않다. 결국 외부 동기화가 필요하다.
- 진짜 동시성 컬렉션 (CopyOnWriteArrayList,
ConcurrentLinkedQueue등) 이 훨씬 우수하다. - 레거시 잔재, JDK 가 retrofit 했으나 이상적인 컬렉션 설계는 아니다.
그러면 언제 만나게 되나
- 레거시 코드 유지보수: 90 년대 후반 ~ 2000 년대 초 코드
Stack<E>사용 코드:Stack이 Vector 를 상속함. 새 코드에선Deque<E> = new ArrayDeque<>()권장- AWT/Swing API:
JTable,JList의 일부 모델이 Vector 사용
대안
| 의도 | 대안 |
|---|---|
| 단일 스레드 List | ArrayList |
| 외부 동기화 List | Collections.synchronizedList(new ArrayList<>()) |
| 읽기 多, 쓰기 거의 없음 | CopyOnWriteArrayList |
| 동시 큐/덱 | ConcurrentLinkedDeque, LinkedBlockingDeque |
| 스택 | Deque<E> = new ArrayDeque<>() |
참고
- Object
- Iterable
- Collection
- List
- ArrayList
- CopyOnWriteArrayList
- ConcurrentHashMap
- fail-fast iterator
- ConcurrentModificationException
- ReentrantLock
- Blocking
- Brian Goetz, Java Concurrency in Practice, §5 Building Blocks
이 글의 용어 (11개)
- [Java] ArrayListjava
- 정의 는 동적 배열 (dynamic array) 로 구현된 . 내부적으로 를 들고 다니며, 용량이 부족해지면 새 배열로 옮긴다. Java 의 가장 흔한 컬렉션 구현체. 인덱스 기…
- [Java] Collectionjava
- 정의 는 그룹으로 묶인 객체들을 표현하는 최상위 인터페이스. JCF (Java Collections Framework) 의 입구이자, / / / 모두 이를 확장한다. 자체는 직접…
- [Java] ConcurrentHashMapjava
- 정의 는 고동시성 환경에서 사용 가능한 구현. JSR-166 (Java 5) 도입, Java 8 에서 내부 구조가 크게 재작성됐다. 과 같은 인터페이스를 제공하면서 thread-…
- [Java] ConcurrentModificationExceptionjava
- 정의 (CME) 는 가 컬렉션의 구조 변경을 탐지했을 때 던지는 unchecked 예외 ( ). 이름과 달리 반드시 동시성 (멀티스레드) 문제일 때만 발생하는 것이 아니다. 단일…
- [Java] CopyOnWriteArrayListjava
- 정의 는 쓰기 시 배열 전체를 복사 하는 thread-safe 구현. 읽기에는 lock 이 전혀 없고, 쓰기에는 으로 직렬화한다. (JSR-166) 의 컬렉션. 읽기 압도적 다,…
- [Java] Fail-Fast Iteratorjava
- 정의 Fail-Fast Iterator 는 순회 도중 컬렉션의 구조가 변경되면 즉시 을 던지는 iterator. , , 등 대부분의 컬렉션의 iterator 가 이 방식. 목적은…
- [Java] Iterablejava
- 정의 는 루프로 순회 가능한 모든 타입의 최상위 인터페이스. 단 하나의 추상 메서드, 를 정의한다. 인터페이스가 을 extends 하므로 , , , 등 모든 컬렉션이 자동으로 대…
- [Java] Listjava
- 정의 는 순서가 있고 중복이 허용 되는 컬렉션을 표현하는 인터페이스. 각 원소는 0 부터 시작하는 정수 인덱스로 접근 가능. 인터페이스를 확장하며, 의 모든 성질을 물려받는다. …
- [Java] Objectjava
- 정의 는 Java 의 모든 클래스의 최상위 부모 (root) 클래스. 가 명시되지 않은 클래스는 컴파일러가 자동으로 를 붙인다. 인터페이스는 클래스가 아니라 를 직접 상속하지는 …
- [Java] ReentrantLockjava
- 정의 는 키워드와 같은 상호 배제 (mutual exclusion) 를 제공하는 클래스 기반 락. JSR-166 (Java 5) 에서 추가됐다. "재진입 (reentrant)" …
- Blocking (블로킹)concurrency
- 정의 Blocking 은 호출이 결과가 준비될 때까지 호출 스레드를 멈춰두는 실행 방식. 멈춰 있는 동안 그 스레드는 다른 일을 할 수 없다. OS 가 스레드를 sleep 상태로…
💬 댓글