[Java] 자바에서는 스택을 잘 안 쓴다고?
포스팅 계기
이미지로 대신한다.
그래서 오늘은 왜 스택을 잘 안 쓰는지, 그럼 대신 뭘 사용하는지에 대해 작성해보려 한다.
Stack은 Vector를 상속받기 때문
자바의 `Stack` 클래스는 `java.util.Vector`를 상속받아 만들어졌다.
바로 이 점에서 문제가 시작된다.
`Vector`는 동기화된(synchronized) 클래스이다.
이는 멀티스레드 환경에서 안전하다는 장점이 있으나, 싱글스레드 환경에서는 불필요한 성능 저하를 일으킨다.
동기화되는 게 왜?
동기화(Synchronization)란?
동기화는 여러 스레드가 공유 자원에 접근할 때, 동시에 접근하지 못하게 막는 것이다.
int cnt = 0;
public void increment() {
cnt++; // 여러 스레드가 동시에 실행하면 문제 발생 가능
}
여러 스레드가 동시에 `cnt++`를 실행하게 되면 덮어쓰기 같은 문제가 발생할 수 있다.
따라서 자바에서는 `synchronized`를 사용한다.
public synchronized void increment() {
cnt++;
}
이렇게 하게 되면 한 번에 하나의 스레드만 이 메서드를 실행할 수 있게 된다.
그런데 왜 성능 저하가?
한 스레드가 실행하고 있는 사이, 다른 스레드는 대기해야 하기 때문이다.
이 대기 시간이 누적되면 성능이 저하된다...만
싱글스레드 환경에서는 '다른 스레드' 자체가 없기 때문에, 대기할 스레드도 없다.
그럼에도!
`synchronized`가 걸린 메서드는 JVM 내부적으로 모니터 락(Monitor Lock)을 관리해야 한다.
그 락을 얻고 해제하는 과정 자체도 비용이 들게 된다.
따라서 ArrayDeque을 사용한다!
`ArrayDeque`은 `Deque` 인터페이스를 구현한 클래스이다.
Deque(Double Ended Queue)은 이름 그대로 양쪽 끝에서 삽입과 삭제가 가능한 자료구조지만,
한쪽만 사용하면 스택처럼 쓸 수 있다.
`ArrayDeque`은
- 내부적으로 배열을 사용해 빠르고
- `synchronized`가 없어 불필요한 비용도 없을 뿐더러
- null 저장도 막아주기 때문에 안전하다.
실제로 Oracle 공식 문서에서도 `Stack`보다 `ArrayDeque` 사용을 권장하고 있다.
A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class.