예시 코드:
public StringBuilder append(String str) {
super.append(str); // 부모인 AbstractStringBuilder의 append 호출
return this;
}
예시 코드:
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null; // 캐시 초기화
super.append(String.valueOf(obj)); // 부모의 append 호출
return this;
}
요약:
StringBuilder와 StringBuffer는 동적 배열 방식으로 내부 버퍼를 관리합니다. 버퍼는 일정한 크기만큼 공간을 할당받아 문자열을 저장하는데, 추가하는 문자열이 현재 버퍼 크기를 초과할 경우, 자동으로 버퍼를 확장합니다. 이때 버퍼는 현재 크기의 약 2배로 증가합니다. 구체적인 과정은 다음과 같습니다:
초기 버퍼 할당: 객체가 생성될 때 기본적으로 크기가 작은 버퍼를 할당합니다. 예를 들어 new StringBuilder()는 초기 버퍼 크기를 16으로 설정합니다.
크기 초과 시 확장: 버퍼 크기를 초과하는 문자열을 추가할 경우, ensureCapacity 메소드가 호출되어 버퍼를 확장합니다. 일반적으로 기존 크기의 2배 + 2 만큼 증가합니다.
배열 복사: 새롭게 확장된 버퍼에 기존 문자열 데이터를 복사합니다.
이러한 방식은 메모리를 효율적으로 사용하면서도 잦은 배열 복사를 피할 수 있는 구조입니다.
효율성 측면에서 이 방식은 적절한 메모리 크기를 미리 설정하여 배열 복사를 최소화하는 것이 중요한데, 대용량 문자열을 다룰 때는 ensureCapacity(int) 메소드를 호출하여 한 번에 큰 버퍼를 확보하는 것이 유리할 수 있습니다.
StringBuffer의 모든 메소드가 synchronized로 동기화되어 있기 때문에, 여러 스레드가 동시에 접근해도 스레드 안전성을 보장합니다. 하지만, 이는 성능 저하를 야기할 수 있습니다. 동기화가 적용되면, 동시에 여러 스레드가 특정 메소드에 접근할 수 없고 한 번에 한 스레드만 접근이 가능합니다. 즉, 스레드가 차례대로 대기해야 하기 때문에 성능에 병목 현상이 발생할 수 있습니다.
장점: 멀티스레드 환경에서 안전하게 동작하기 때문에 데이터 불일치 문제나 경쟁 조건을 방지할 수 있습니다.
단점: 동기화 오버헤드로 인해 단일 스레드 환경에서는 성능이 떨어지게 됩니다. 불필요한 동기화가 추가되기 때문에 StringBuilder에 비해 성능이 느려질 수 있습니다.
사용 권장 환경: 멀티스레드 환경에서 여러 스레드가 같은 인스턴스에 접근할 가능성이 있는 경우, StringBuffer의 동기화는 매우 유용합니다. 하지만 스레드 간 자원 공유가 없는 환경에서는 동기화가 불필요하므로 StringBuilder를 사용하는 것이 성능 상 유리합니다.
Q3: Java에서 멀티스레드 환경을 고려할 때, StringBuffer 대신 다른 동기화 전략은 어떤 것이 있을까요?
StringBuffer 대신 동기화 전략을 직접 구현하거나, 자바에서 제공하는 다른 동시성 도구를 활용할 수 있습니다.
이처럼 멀티스레드 환경에서 StringBuffer 외에도 다양한 동기화 전략과 도구가 있습니다. 사용 목적과 환경에 따라 가장 적합한 도구를 선택하는 것이 중요합니다.