Study/Java

[JAVA] StringBuilder와 StringBuffer 비교

짱이08 2023. 11. 22. 14:04

String은 불변 객체이기 때문에 문자열 연산이 필요할 경우 StringBuilder와 StringBuffer를 사용한다는 것을 이전에 포스팅을 통해 알 수 있었습니다.

https://yoozzang.tistory.com/entry/%EB%B0%98%EB%B3%B5%EB%AC%B8loop-%EC%95%88%EC%97%90%EC%84%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1%EA%B3%BC-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90

 

반복문(loop) 안에서 객체 생성과 성능 비교

프로그램 성능 최적화를 고려할 때, 객체의 생성과 메모리 관리는 핵심적인 주제입니다. 특히 반복 작업에서 빈번한 객체 생성은 메모리 할당과 관리에 부담을 주며, 가비지 컬렉션의 작동으로

yoozzang.tistory.com

 

두 클래스 모두 내부적으로 문자 배열(char array)을 사용하여 문자열 데이터를 저장합니다.

그렇다면 StringBuilder와 StringBuffer의 차이점은 무엇일까요?
실제 코드를 통해서 어떻게 되는지 확인 해 보도록하겠습니다.

차이점

동기화

StringBuffer는 동기화됩니다.
이는 멀티스레드 환경에서 여러 스레드가 동시에 StringBuffer 객체에 접근할 때 안전하게 사용될 수 있다는 것을 의미합니다.
동기화는 StringBuffer의 메서드들이 synchronized 키워드로 표시되어 있다는 것을 의미하며, 이로 인해 성능이 약간 느려질 수 있습니다.

 

성능

StringBuilder는 StringBuffer와 비슷하지만 동기화되지 않습니다.
동기화가 필요 없는 단일 스레드 환경에서 StringBuilder를 사용하는 것이 더 효율적입니다.
동기화 오버헤드가 없기 때문에 StringBuilder가 StringBuffer보다 빠릅니다.

 

메서드

두 클래스는 거의 동일한 메서드 세트를 제공합니다.
문자열을 추가(append), 삽입(insert), 뒤집기(reverse) 등의 작업을 수행할 수 있습니다.
메서드의 기능 자체는 두 클래스에서 동일하지만 StringBuffer의 메서드들은 synchronized입니다.

 

사용 사례

멀티스레드 환경에서는 StringBuffer의 동기화 기능이 중요합니다.
반면, 단일 스레드 환경이나 성능이 중요한 애플리케이션에서는 StringBuilder가 더 적합합니다.

즉, 차이는 동기화의 여부입니다.

 

두 클래스에서 가장 많이 사용하는 메서드인 append 메서드를 살펴보겠습니다.

// StringBuilder
@Override
    public synchronized StringBuffer append(char[] str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

// StringBuffer
@Override
    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }

내부 구현은 같으나 StringBuilder에는 synchronized 키워드가 항상 붙습니다.
이는 멀티스레드 환경에서 하나의 스레드만 해당 메소드에 접근 할 수 있다는 것을 의미합니다.

public class MultiThreadSB {
    public static void main(String[] args) throws InterruptedException {
        StringBuffer stringBuffer = new StringBuffer();
        StringBuilder stringBuilder = new StringBuilder();

        Runnable r = () -> {
            for (int i = 0; i < 1000; i++) {
                stringBuffer.append("X");
                stringBuilder.append("X");
            }
        };

        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println("StringBuffer length: " + stringBuffer.length()); // 항상 2000
        System.out.println("StringBuilder length: " + stringBuilder.length()); // 2000 미만일 수 있음

    }
}
  • 결과
    StringBuffer length: 2000
    StringBuilder length: 1971

Runnable인터페이스의 run() 메소드를 StringBuilder와 StringBuffer에 X를 1000번 더하는 것으로 구현하였습니다.

두개의 스레드를 생성하여 실행을 하면 StringBuffer는 length가 2000이 나오고 StringBuilder는 length가 실행마다 변경됩니다.
이는 StringBuilder가 멀티스레드 환경에서 동시성이슈로 thread-safe하지 않다는 것을 의미합니다.

 


위의 코드를 통해 직접 멀티스레드 환경에서 StrignBuffer를 사용하는 것이 개발자가 의도한 대로 코드가 실행되는 것인 것을 확인 할 수 있었습니다.
따라서 멀티스레드 환경에서는 StringBuffer를 쓰고, 싱글스레드 환경에서는 StringBuilder를 사용하여
조금 더 안정적이고 효율적으로 프로그램을 개발 해야합니다.