개발 메모장

[Java] String, StringBuffer, StringBuilder의 특징 및 차이점 본문

Java

[Java] String, StringBuffer, StringBuilder의 특징 및 차이점

yyyyMMdd 2023. 11. 29. 15:35
728x90

#. 3가지 모두 문자열에 관련되어 저장, 관리를 위한 클래스입니다.

 

#. 보통 간단한 것에 대해선 String만 사용하는데 상황에 따라 버퍼와 빌더를 사용해야 할 때가 있을 것입니다.

 


#. String

- 불변성을 가지기에 수정할 수 없고 메모리 할당 공간도 변하지 않습니다.
    - 보통 수정하는 것처럼 보이는 작업들은 사실 새로운 String 개체들을 만들어 저장하는 방식으로 진행됩니다.

    - 그렇기에 기존 String 개체는 수정되는 것이 아니고, 메모리 할당 공간을 추가로 차지하게 되어 지속적으로 수정 시 성능 저하가 발생할 수 있습니다.

 

- 주로 간단한 처리에 사용됩니다.(문자 연산 - +, concat() 등)


- 불변성을 가지기에 스레드로부터 안전합니다.


- 수정이 불가하기에 비밀번호 같은 민감 정보 저장에 사용하는 것이 좋습니다.


  •     Example
String tester = "test";
tester = "tester";


    - 문자열 변수 tester는 최초 test라고 되어있고 이를 tester로 수정하였습니다.

 

    - 이 경우 tester = "test"와 tester = "tester" 모두 메모리 상에선 살아있다는 것입니다.

    

    - 기존 tester = "test"는 GC의 제거 대상이 됩니다.

 


 

#. StringBuffer

- 가변성을 가지며 새 개체를 생성하지 않고도 문자열을 수정할 수 있는 것이 String과의 가장 큰 차이입니다.


- 멀티스레드 환경에서 동기화를 지원하기 때문에 스레드로부터 안전합니다.

 

- 경합 조건에 따른 예측불가의 동작

    - 공유 데이터, 리소스에 여러 개의 스레드가 접근 시 경합 조건이 달라질 수 있으며, 이에 따라 예측불가한 잘못된 동작이 발생할 수 있습니다.


- 데이터 손상

    - 여러 스레드가 동일 데이터에 접근해 수정하려 할 때 잘못된 수정을 초래해 데이터가 불일치하게 되는 경우가 발생할 수 있습니다.


- 동기화로 인해 단일 스레드에선 속도가 느리나 멀티스레드 상황에서 안전하게 사용이 가능합니다.


  • Example

- 아래 소스는 멀티스레드 상태에서 하나의 StringBuffer를 수정하는 작업을 합니다.

 

- thread1과 tread2가 각각 task를 처리하며 각각 A가 1000번씩 더해지므로 thread2가 끝나는 시점엔 length는 2000이 됩니다.
(아래의 StringBuilder와 비교 필수!)

public class StringBufferTester {
	private static StringBuffer sbuffer = new StringBuffer();
    
    public static void main(String[] args) {
    	SpringApplication.run(StringBufferTester.class, args);
        
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                sbuffer.append("A");
            }
        };

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

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

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

        System.out.println("Final StringBuffer length: " + sbuffer.length());
    }
}

 


 

#. StringBuilder

 

- 가변성을 가지기에 새 객체를 생성하지 않고 수정 가능하나 동기화 지원이 되지 않아 스레드로부터 안전하지 않습니다.


- 비동기적 특성을 지니며 단일스레드에서 가장 성능이 좋습니다.


- 속도는 빠르나 멀티스레드 상황에서 안전하지 않습니다.


  • Example

- 위 StringBuffer와 같은 소스이며 StringBuffer 대신 StringBuilder를 사용해 봤습니다.

 

- append 한 횟수(sbuilder.length)의 값이 초기화한 후 실행 할 때마다 매번 다릅니다.

 

- thread1과 thread2에 대한 값이 동기화되지 않고 처리되면서 오차가 발생하게 됩니다.

public class StringBuilderTester {
	private static StringBuilder sbuilder = new StringBuilder();
    
    public static void main(String[] args) {
    	SpringApplication.run(StringBuilderTester.class, args);
        
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                sbuilder.append("A");
            }
        };

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

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

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

        System.out.println("Final StringBuilder length: " + sbuilder.length());
    }
}

 


 

#. 정리해 보자면 아래와 같습니다.

 

    - String - 자주 변경하지 않는 객체나 보안에 민감한 데이터에 사용하면 좋습니다.


    - StringBuffer - 멀티스레드 환경에서 스레드로부터 안전함을 보장받고자 할 때 사용하면 좋습니다.


    - StringBuilder - 단일스레드 환경에서 스레드 이슈 우려가 없고 빠른 성능을 보장받고자 할 때 사용하면 좋습니다.

 

 

 

===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================

728x90