1. 程式人生 > 其它 >為什麼(1),Java架構師技術進階路線圖

為什麼(1),Java架構師技術進階路線圖

為什麼(1),Java架構師技術進階路線圖

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
sb.append("a");
}
}).start();
}
// 睡眠確保所有執行緒都執行完
Thread.sleep(1000);
System.out.println(sb.length());
}


上述業務邏輯比較簡單,就是構建一個StringBuilder,然後建立10個執行緒,每個執行緒中拼接字串“a”1000次,理論上當執行緒執行完成之後,列印的結果應該是10000才對。

但多次執行上面的程式碼列印的結果是10000的概率反而非常小,大多數情況都要少於10000。同時,還有一定的概率出現下面的異常資訊“

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:826)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.secbro2.strings.StringBuilderTest.lambda$test$0(StringBuilderTest.java:18)
at java.lang.Thread.run(Thread.java:748)
9007


# 執行緒不安全的原因

StringBuilder中針對字串的處理主要依賴兩個成員變數char陣列value和count。StringBuilder通過對value的不斷擴容和count對應的增加來完成字串的append操作。

// 儲存的字串(通常情況一部分為字串內容,一部分為預設值)
char[] value;

// 陣列已經使用數量
int count;


上面的這兩個屬性均位於它的抽象父類AbstractStringBuilder中。

如果檢視構造方法我們會發現,在建立StringBuilder時會設定陣列value的初始化長度。

public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}


預設是傳入字串長度加16。這就是count存在的意義,因為陣列中的一部分內容為預設值。

當呼叫append方法時會對count進行增加,增加值便是append的字串的長度,具體實現也在抽象父類中。

public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}


我們所說的執行緒不安全的發生點便是在append方法中count的“+=”操作。我們知道該操作是執行緒不安全的,那麼便會發生兩個執行緒同時讀取到count值為5,執行加1操作之後,都變成6,而不是預期的7。這種情況一旦發生便不會出現預期的結果。

# 拋異常的原因

回頭看異常的堆疊資訊,<typo id="typo-2537" data-origin="回" ignoretag="true">回</typo>發現有這麼一行內容:

at java.lang.String.getChars(String.java:826)


對應的程式碼就是上面AbstractStringBuilder中append方法中的程式碼。對應方法的原始碼如下:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}


其實異常是最後一行arraycopy時JVM底層發生的。arraycopy的核心操作就是將傳入的String物件copy到value當中。

而異常發生的原因是明明value的下標只到6,程式卻要訪問和操作下標為7的位置,當然就跑異常了。

那麼,為什麼會超出這麼一個位置呢?這與我們上面講到到的count被少加有關。在執行str.getChars方法之前還需要根據count校驗一下當前的value是否使用完畢,如果使用完了,那麼就進行擴容。append中對應的方法如下:

ensureCapacityInternal(count + len);


ensureCapacityInternal的具體實現:

private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,

最後

面試是跳槽漲薪最直接有效的方式,馬上金九銀十來了,各位做好面試造飛機,工作擰螺絲的準備了嗎?

掌握了這些知識點,面試時在候選人中又可以奪目不少,暴擊9999點。機會都是留給有準備的人,只有充足的準備,才可能讓自己可以在候選人中脫穎而出。

如果你需要這份完整版的面試筆記,只需你多多支援我這篇文章。

——對文章進行點贊+評論,關注我,然後再點選這裡免費領取