StringBuilder 스레드가 안전하지 않은 이유, StringBuffer 스레드가 안전한 이유
2981 단어 JDK 소스
라인 안전 문제에 관해서는 그들의 jdk 원본을 보아야 한다.
1. StringBuilder
public StringBuilder append(String str) {
super.append(str);
return this;
}
//
char[] value;
//
int count;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
//
ensureCapacityInternal(count + len);
// str value
str.getChars(0, len, value, count);
count += len;
return this;
}
우리는count+=len이 원자 조작이 아니라 여러 개의 라인을 조작할 때 기대치와 어긋나는 상황이 발생하여 전체count가 기대보다 작다는 것을 알 수 있다.
두 번째 문제는 Array Index Out Of Bounds Exception 이상을 던질 수 있다는 것이다. 예를 들어 두 개의 라인이 append 조작을 하고 라인 1이 ensure Capacity Internal 확장을 한 후에 타임 필름이 다 떨어졌다. 이때 라인 2가 append를 실행하고 용량을 마침 다 썼다. 라인 1이 실행된str.get Chars를 기다리면 Array Index Out Of Bounds Exception 이상을 던진다.
/**
* This method has the same contract as ensureCapacity, but is
* never synchronized.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
// 2 2
int newCapacity = value.length * 2 + 2;
// str.length()+value.length
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
// ,
value = Arrays.copyOf(value, newCapacity);
}
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
2. StringBuffer
StringBuilder와 마찬가지로 StringBuffer도 AbstractStringBuilder의 하위 클래스입니다.다른 것은 StringBuffer가 append와 delete 작업을 할 때 동기화 자물쇠를 넣었다는 것이다.
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
public synchronized StringBuffer delete(int start, int end) {
toStringCache = null;
super.delete(start, end);
return this;
}
참조: