String/StringBuilder/StringBuffer 차이, thread safe란?, String이 불변객체인 이유

노을·2022년 3월 5일
1

java

목록 보기
2/9
post-thumbnail

String

String str = "hello"; 
str = str + " world"; 

  • 불변의 속성을 갖는다.
  • 기존에 hello 값이 들어가있던 String 클래스의 참조변수 strhello world라는 값을 가지고 있는 새로운 메모리 영역을 가리키게 변경된다.
  • 처음 선언했던 hello로 값이 할당되어 있던 메모리 영역은 Garbage로 남아있다가 GC(garbage collection)에 의해 사라지게 되는 것이다.
  • 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성
  • 불변객체이기 때문에 StringBuffer처럼 멀티스레드 환경에서 안전하다.
  • 사용하는 경우 : 문자열 연산이 적고 멀티스레드 환경일 경우
  • 단점 : 문자열 연산을 많이 할 수록 객체 수가 늘어나 성능이 떨어진다.




StringBuffer / StringBuilder

StringBuffer sb= new StringBuffer("hello"); 
sb.append(" world");

  • 가변성을 가진다.
  • 문자열 연산 등으로 기존 객체의 공간이 부족하게 되는 경우 기존의 버퍼 크기를 늘리며 유연하게 동작한다.
  • .append() .delete() 등의 API를 이용하여 동일 객체내에서 문자열을 변경하는 것이 가능하다.
  • 사용하는 경우 : 문자열의 추가, 수정, 삭제가 빈번하게 발생할 경우 사용
  • 단점 : 초기 buffer의 크기 설정, 버퍼 크기 줄이고 늘리며 객체 생성 속도가 느리다.


StringBuffer

  • 동기화 키워드(synchronized keyword)를 지원하여 멀티쓰레드 환경에서 안전하다.
  • 사용하는 경우 : 문자열 연산이 많고 멀티스레드 환경일 경우

StringBuilder

  • 동기화를 지원하지 않지만 단일쓰레드에서의 성능은 StringBuffer 보다 뛰어나다.
  • 사용하는 경우 : 문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우




thread safe란?

멀티 스레드 프로그래밍에서 일반적으로 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 뜻한다.

Thread-safe를 지키기 위한 방법

  1. Re-entrancy

    어떤 함수가 한 스레드에 의해 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하더라도 그 결과가 각각에게 올바로 주어져야 한다.

  2. Thread-local storage

    공유 자원의 사용을 최대한 줄여 "각각의 스레드에서만 접근 가능한 저장소"들을 사용함으로써 동시 접근을 막는다.

    이 방식은 동기화 방법과 관련되어 있고, 또한 공유상태를 피할 수 없을 때 사용하는 방식이다.

  3. Mutual exclusion

    Thread에 lock이나 semaphore를 걸어서 공유자원에는 하나의 thread만 접근 가능하게 한다.

  4. Atomic operations

    데이터 변경시 atomic하게 데이터에 접근하도록 만든다.

  5. Immutable Object

    객체 생성 이후에 값을 변경할 수 없도록 만든다.




java String 불변객체인 이유


  1. 성능(Performance)

    String이 불변이기 때문에 String Pool도 존재할 수 있다.

    자바에서는 다른 변수여도 값이 같으면 같은 객체를 참조한다. → 힙 영역 절약

    public class Test {
        public static void main(String[] args) {
            String s1 = "Hello World";
            String s2 = "Hello World";
    
            System.out.println(s1 == s2);  // true
        }
    }

    만약 String이 가변 객체였다면 s2 값을 변경해도 s1 == s2true로 나온다. → 객체 공유 불가능

  2. 동기화(Synchronization)

    불변 객체는 값이 바뀔 일이 없기 때문에 멀티스레드 환경에서 Thread-safe하다는 장점이 있다.

  3. 해시코드 캐싱(Hashcode Caching)

    String의 hashCode() 구현을 보면, 아직 hash 값을 계산한 적이 없을 때 최초 1번만 실제 계산 로직을 수행한다. 이후부터는 이전에 계산했던 값을 그냥 리턴만 하도록 되어 있다. 즉 hashCode 값을 캐싱(caching)하고 있다. → 성능 개선

    public final class String {
        public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
    }

    캐싱할 수 있는 이유는 String이 불변객체이기 때문이다.

    참고로 hashCode()Hash 자료구조의 구현체, 예를 들면 HashMap, HashTable, HashSet와 같은 클래스에서 자주 호출된다.

  4. 보안

    사용자의 이름이나 패스워드, 혹은 네트워크 연결을 위한 포트 번호나 connection URL, 파일 이름 등 중요한 정보를 String으로 받을 때가 많다. JVM의 class loaderclass loading을 수행할 때도 마찬가지다.

    객체에 대해 보안 검사를 해도, 가변 객체라면 나중에 문자열이 변경될 가능성이 있다.





출처:

https://velog.io/@hoo00nn/Thread-safe와-동기화-객체

https://ifuwanna.tistory.com/221

https://devlog-wjdrbs96.tistory.com/247

0개의 댓글