본문 바로가기
Java

자바 StringBuilder와 StringBuffer는 뭐가 다를까?

by 긍고 2024. 9. 20.
반응형

개요


최근 개발을 하며 문자열을 여러 개 이어붙일 일이 있었는데, 기존 코드를 보니 StringBuilder를 많이 사용하는것을 볼 수 있었다. 하지만 해당 프로젝트 외에 다른 프로젝트에서는 StringBuffer를 사용해서 문자열을 이어 붙였었는데 두 클래스간 어떤 차이가 있을지 궁금하여 비교해서 정리하려고 한다.

 

StringBuilder와 StringBuffer


일단 StringBuilderStringBuffer는 모두 문자열을 수정할 수 있는 가변 클래스이다. 둘 다 문자열을 효율적으로 처리할 수 있도록 설계되었으나, 사용 목적에 따라 선택해야 한다. 두 클래스의 대표적인 큰 차이는 아래와 같다.

 

동기화(Synchronization) 여부

  • StringBuilder: 동기화되지 않은 클래스. 멀티스레드 환경에서 사용할 경우 주의가 필요함.
  • StringBuffer: 동기화된 클래스. 멀티스레드 환경에서 안전하게 사용할 수 있음.
// StringBuffer는 동기화되어 있음
StringBuffer stringBuffer = new StringBuffer("Hello");
stringBuffer.append(" World");
System.out.println(stringBuffer);  // 출력: Hello World

// StringBuilder는 동기화되어 있지 않음
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuilder.append(" World");
System.out.println(stringBuilder);  // 출력: Hello World

 

성능 비교

두 클래스는 위 코드를 통해 확인할 수 있듯이 기능을 동일하나 속도에서 차이를 보인다. 동기화된 StringBuffer는 스레드 간 충돌을 방지하지만, 그 대가로 성능이 떨어진다. 반면, StringBuilder는 동기화가 없기 때문에 단일 스레드에서 StringBuffer보다 더 빠른 성능을 보인다. 성능을 최우선으로 고려해야 하는 단일 스레드 환경에서는 StringBuilder가 훨씬 유리하다고 볼 수 있다.

 

언제 StringBuilder와 StringBuffer를 사용해야 할까?


 

  • 멀티스레드 환경: 여러 스레드가 동시에 문자열을 처리해야 한다면, StringBuffer를 사용. 동기화된 메서드를 통해 데이터 일관성을 유지할 수 있음.
  • 단일 스레드 환경: 성능이 중요하고 동기화가 필요 없는 경우에는 StringBuilder가 적합. 주로 웹 애플리케이션이나 단일 스레드 프로그램에서 성능을 최적화할 때 사용됨.

 

멀티쓰레드 환경 예제


public class StringExample {
    public static void main(String[] args) {
        // StringBuffer 예제 (동기화된 상태)
        StringBuffer buffer = new StringBuffer();
        Runnable task1 = () -> {
            for (int i = 0; i < 100; i++) {
                buffer.append(i);
            }
        };

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

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

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("StringBuffer 결과: " + buffer.length());

        // StringBuilder 예제 (동기화되지 않은 상태)
        StringBuilder builder = new StringBuilder();
        Runnable task2 = () -> {
            for (int i = 0; i < 100; i++) {
                builder.append(i);
            }
        };

        Thread thread3 = new Thread(task2);
        Thread thread4 = new Thread(task2);

        thread3.start();
        thread4.start();

        try {
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("StringBuilder 결과: " + builder.length());
    }
}

 

 

위처럼 코드를 작성하고 실행한다면 아래와 같은 결과가 도출될 수 있다.

  • StringBuffer의 경우, 동기화가 되어 있어 스레드 간 충돌이 없으며 따라서 두 스레드가 안전하게 작업을 완료하고, buffer의 길이는 예상대로 200이 됨.
StringBuffer 결과: 200
  • StringBuilder의 경우, 동기화가 없기 때문에 스레드 간 데이터 충돌이 발생할 수 있으며. 그 결과 builder의 길이는 200보다 작을 수 있음. 예를 들어, 스레드가 충돌하여 일부 숫자가 누락되면 결과는 다음과 같이 나올 수 있다.
StringBuilder 결과: 157  // 실제 결과는 상황에 따라 달라질 수 있습니다.

 

정리


StringBuilder와 StringBuffer는 모두 문자열을 처리할 때 유용하지만, 그 사용 시나리오는 다르다. 단일 스레드 환경에서는 StringBuilder를 사용하여 성능을 최적화하고, 멀티스레드 환경에서는 동기화가 보장되는 StringBuffer를 사용하여 데이터의 일관성을 유지해야 하는 편이 좋다.

 

결론적으로, 본인이 개발하는 애플리케이션의 요구사항에 따라 적절한 클래스를 선택하는 것이 중요하며 문자열을 처리하는 상황에서 어떤 클래스가 적합한지 신중하게 고려하고 사용하면 될 것 같다.

'Java' 카테고리의 다른 글

[Java] 공통 적용 JsonDeserializer 만들어 보기  (4) 2024.09.23
[Java] UUID를 이용한 고유 식별자 생성  (1) 2024.09.22
Cursor<T>  (0) 2023.03.09
[Java] Spring Boot Actuator  (0) 2023.02.14
[Spring Cloud] HA of Service Discovery  (0) 2023.02.07

댓글