1. 程式人生 > >為什麼StringBuilder是執行緒不安全的?StringBuffer是執行緒安全的?

為什麼StringBuilder是執行緒不安全的?StringBuffer是執行緒安全的?

面試中經常問到的一個問題:StringBuilderStringBuffer的區別是什麼?
我們非常自信的說出:StringBuilder是執行緒安全的,StirngBuffer是執行緒不安全的
面試官:StringBuilder不安全的點在哪兒?
這時候估計就啞巴了。。。

分析

StringBufferStringBuilder的實現內部是和String內部一樣的,都是通過 char[]陣列的方式;不同的是Stringchar[]陣列是通過final關鍵字修飾的是不可變的,而StringBufferStringBuilderchar[]陣列是可變的。

首先我們看下邊這個例子:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 10000; i++){
            new Thread(() -> {
                for (int j = 0; j < 1000; j++){
                    stringBuilder.append("a");
                }
            }).start();
        }

        Thread.sleep(100L);
        System.out.println(stringBuilder.length());
    }
}

直覺告訴我們輸出結果應該是10000000,但是實際執行結果並非我們所想。

從上圖可以看到輸出結果是9970698,並非是我們預期的1000000,並且還丟擲了一個異常ArrayIndexOutOfBoundsException{非必現}

為什麼輸出結果並非預期值?

我們先看一下StringBuilder的兩個成員變數(這兩個成員變數實際上是定義在AbstractStringBuilder裡面的,StringBuilderStringBuffer都繼承了AbstractStringBuilder

StringBuilderappend方法

StringBuilderappend方法呼叫了父類的append

方法

我們直接看第七行程式碼,count += len; 不是一個原子操作,實際執行流程為

  • 首先載入count的值到暫存器
  • 在暫存器中執行 +1操作
  • 將結果寫入記憶體

假設我們count的值時10len的值為1,兩個執行緒同時執行到了第七行,拿到的值都是10,執行完加法運算後將結果賦值給count,所以兩個執行緒最終得到的結果都是11,而不是12,這就是最終結果小雨我們預期結果的原因。

為什麼會丟擲ArrayIndexOutOfBoundsException異常?

我們看回AbstractStringBuilder的追加()方法原始碼的第五行,ensureCapacityInternal()方法是檢查StringBuilder的物件的原字元陣列的容量能不能盛下新的字串,如果盛不下就呼叫expandCapacity()方法對字元陣列進行擴容。

private  void  ensureCapacityInternal(int  minimumCapacity)  {
         //溢位意識程式碼
    if  (minimumCapacity  -  value .length>  0)
        expandCapacity(minimumCapacity); 
}

擴容的邏輯就是新一個新的字元陣列,新的字元陣列的容量是原來字元陣列的兩倍再加2,再通過System.arryCopy()函式將原陣列的內容複製到新陣列,最後將指標指向新的字元陣列。

void  expandCapacity(int  minimumCapacity)  {
     //計算新的容量
    int  newCapacity =  value .length *  2  +  2 ; 
    //中間省略了一些檢查邏輯
     ...
     value  = Arrays.copyOf( value,newCapacity); 
}

Arrys.copyOf()方法

public  static  char []  copyOf(char [] original,  int  newLength)  {
     char [] copy =  new  char [newLength]; 
    //拷貝陣列
     System.arraycopy(original,  0,copy,  0,
                         Math.min(original.length,newLength)); 
    返回  副本; 
}

AbstractStringBuilder的追加()方法原始碼的第六行,是將字串物件裡面炭數組裡面的內容拷貝到StringBuilder的物件的字元數組裡面,程式碼如下:

str.getChars(0,len,  value,count);

則GetChars()方法

public  void  getChars(int  srcBegin,  int  srcEnd,  char  dst [],  int  dstBegin)  {
     //中間省略了一些檢查
     ...   
    System.arraycopy( value,srcBegin,dst,dstBegin,srcEnd  -  srcBegin); 
}

拷貝流程見下圖

假設現在有兩個執行緒同時執行了StringBuilderappend()方法,兩個執行緒都執行完了第五行的ensureCapacityInternal()方法,此刻count=5

這個時候執行緒1cpu時間片用完了,執行緒2繼續執行。執行緒2執行完整個append()方法後count變成6了。

執行緒1繼續執行第六行的str.getChars()方法的時候拿到的count值就是6了,執行char[]陣列拷貝的時候就會丟擲ArrayIndexOutOfBoundsException異常。

至此,StringBuilder為什麼不安全已經分析完了。如果我們將測試程式碼的StringBuilder物件換成StringBuffer物件會輸出什麼呢?

結果肯定是會輸出 1000000,至於StringBuffer是通過什麼手段實現執行緒安全的呢?看下原始碼就明白了了。。。

相關推薦

C#(Winform) 當前執行在單執行單元中,因此無法例項化 ActiveX 控制元件

解決方案: 1、在主執行緒中例項化此ActiveX控制元件 2、將建立此Active控制元件的執行緒設定為單執行緒。       Thread.ApartmentState 獲取或設定此執行緒的單元狀態。             newThread= new Thre

在Application的onCreate中建立執行執行能被執行

問題: 在Application的onCreate中呼叫了 ExecutorService service = Executors.newSingleThreadExecutor(); serv

String,StringBufferStringBuilder的區別|執行安全執行安全

轉載自https://www.cnblogs.com/xingzc/p/6277581.html侵權刪 String 字串常量 StringBuffer 字串變數(執行緒安全) StringBuilder 字串變數(非執行緒安全)  簡要的說, String 型別和 StringBuf

程式碼驗證java的StringBuffer執行安全StringBuilder執行安全

package practice; //通過反轉字串來驗證StringBuffer是執行緒安全而StringBuilder是執行緒不安全的 public class Practice { public static void main(String

為什麼StringBuilder執行安全的?StringBuffer執行安全的?

面試中經常問到的一個問題:StringBuilder和StringBuffer的區別是什麼? 我們非常自信的說出:StringBuilder是執行緒安全的,StirngBuffer是執行緒不安全的 面試官:StringBuilder不安全的點在哪兒? 這時候估計就啞巴了。。。 分析 StringBuffer

Java:為什麼說StringBuilder執行安全

一、前言 可能大家在學習java的基礎過程中,都知道StringBuilder相對StringBuffer效率更高,但是執行緒不安全。可是,到底是怎麼個不安全法,反正我是懵的。藉此機會,寫個小程式碼測試下。 二、編碼 既然是測試StringBuilder和StringB

Java:程式碼驗證 StringBuffer 執行安全StringBuilder執行安全

本文內容大多基於官方文件和網上前輩經驗總結,經過個人實踐加以整理積累,僅供參考。 首先需要明白什麼是執行緒安全,什麼是非執行緒安全,引用百度百科 下面用程式碼進行驗證: 1 定義一個執行緒類,迴圈執行 StringBuffer 和 String

HashMap執行安全的表現 -- Java 8

HashMap執行緒不安全的表現 -- Java 8 先來看看HashMap.put方法的原始碼 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }

執行中的佇列一定需要執行安全

兩個執行緒,主執行緒中update update(){   while(queue.count >0){     //process....     queue.pop()   } }   子執行緒中: queue.enqueue(data)   這樣做是沒有問

SimpleDateFormat是執行安全的!!(NumberFormatException: multiple points)

問題描述: 有兩個專案,一個ssmp、一個性能資料提供perf-provider ,後者給前者提供rest api; 突然有一天,來了新需求,ssmp在短時間內需要傳送大量的rest請求,請求中有一個 時間引數,傳到後臺做時間格式化時開始報錯: 嚴重: Servlet.service

java中為什麼Hashtable是執行安全的,而HashMap是執行安全的?還有ArrayList為什麼是執行安全的,Vector是執行安全的??

文章目錄 一、HashMap解析 二、Hashtable解析 三、Collections.synchronizedMap()解析 四、ConcurrentHashMap 六、ArrayList為什麼是執行緒不安全的,Vector是執行緒安全的?

java.text.DateFormat 執行安全問題

java.text下的 DateFormat 是執行緒不安全的; 建議1: 1、使用threadLocal包裝DateFormat(太複雜,不推薦) 2、使用org.apache.commons.lang3.time.DateFormatUtils下的方法(推薦) DateFormatUtils

simpleDateFormat執行安全

simpleDateFormat是我們比較常用的日期轉換類,但是它是一個執行緒不安全的類。 舉例證明 public class DateFormatExample1 { //請求總數 private static int clientTotal = 1000; //同時允許執行的執

HashMap為什麼是執行安全的?

一直以來只是知道HashMap是執行緒不安全的,但是到底HashMap為什麼執行緒不安全,多執行緒併發的時候在什麼情況下可能出現問題? HashMap底層是一個Entry陣列,當發生hash衝突的時候,hashmap是採用連結串列的方式來解決的,在對應的陣列位置存放連結串列

SimpleDateFormat執行安全導致的問題

轉載 執行緒安全日期格式化操作的幾種方式 由於 DateFormat 是非執行緒安全的,因此在多執行緒併發情況下日期格式化時需要特別注意。下面記錄幾種格式化的方式: 執行緒不安全的處理方式 private static final DateFormat DATE_

為什麼說ArrayList是執行安全的?

概要介紹 首先說一下什麼是執行緒不安全:執行緒安全就是多執行緒訪問時,採用了加鎖機制,當一個執行緒訪問該類的某個資料時,進行保護,其他執行緒不能進行訪問直到該執行緒讀取完,其他執行緒才可使用。不會出現資料不一致或者資料汙染。執行緒不安全就是不提供資料訪問保護,有可能出現多個執行緒先後更改資料

Java併發程式設計 之 HashMap執行安全

我想在平時的多執行緒程式設計中,容器的使用是很普遍的,但是你有沒有考慮過有些容器是不安全的,如Haspmap、ArrayList。這裡講解一下Hashmap不安去體現在哪裡。 插入時不安全: 如果有兩個執行緒A和B,都進行插入資料,剛好經過雜湊計算後得到的雜湊碼是一樣的,即插入的

p6spy列印sql日誌執行安全導致的生產問題

  首先說明下我這個標題可能起的不到位,其實我本次要介紹的是一次生產定位問題的思路及過程。 1.生產現象    國慶前期釋出了一個很小版本,大家都以為沒什麼問題,可是釋出後生產出現了問題並且持續了兩個小

java-雙重檢查鎖為什麼多執行安全

如下程式碼所示: public class doubleCheck{ private static Instance instance; public static Instance getInstance(){ if(instance==null){ //1

ThreadLocal使用注意:執行安全,可能會發生記憶體洩漏

先說可能會發生記憶體洩漏: 前言 ThreadLocal 的作用是提供執行緒內的區域性變數,這種變數線上程的生命週期內起作用,減少同一個執行緒內多個函式或者元件之間一些公共變數的傳遞的複雜度。但是如果濫用ThreadLocal,就可能會導致記憶體洩漏。下面,我們將圍繞三個