String,StringBuffer與StringBuilder的區別|線程安全與線程不安全

String 字符串常量
StringBuffer 字符串變量(線程安全)
StringBuilder 字符串變量(非線程安全)

 簡要的說, String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象, 所以在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,而後將指針指向新的 String 對象,因此常常改變內容的字符串最好不要用 String ,由於每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了之後, JVM 的 GC 就會開始工做,那速度是必定會至關慢的。
 而若是是使用 StringBuffer 類則結果就不同了,每次結果都會對 StringBuffer 對象自己進行操做,而不是生成新的對象,再改變對象引用。因此在通常狀況下咱們推薦使用 StringBuffer ,特別是字符串對象常常改變的狀況下。而在某些特別狀況下, String 對象的字符串拼接實際上是被 JVM 解釋成了 StringBuffer 對象的拼接,因此這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是如下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:
 String S1 = 「This is only a」 + 「 simple」 + 「 test」;
 StringBuffer Sb = new StringBuilder(「This is only a」).append(「 simple」).append(「 test」);
 你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 竟然速度上根本一點都不佔優點。其實這是 JVM 的一個把戲,在 JVM 眼裏,這個
 String S1 = 「This is only a」 + 「 simple」 + 「test」; 其實就是:
 String S1 = 「This is only a simple test」; 因此固然不須要太多的時間了。但你們這裏要注意的是,若是你的字符串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:
String S2 = 「This is only a」;
String S3 = 「 simple」;
String S4 = 「 test」;
String S1 = S2 +S3 + S4;
這時候 JVM 會規規矩矩的按照原來的方式去作

在大部分狀況下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer線程安全的可變字符序列。一個相似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但經過某些方法調用能夠改變該序列的長度和內容。
可將字符串緩衝區安全地用於多個線程。能夠在必要時對這些方法進行同步,所以任意特定實例上的全部操做就好像是以串行順序發生的,該順序與所涉及的每一個線程進行的方法調用順序一致。
StringBuffer 上的主要操做是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。
例如,若是 z 引用一個當前內容是「start」的字符串緩衝區對象,則此方法調用 z.append("le") 會使字符串緩衝區包含「startle」,而 z.insert(4, "le") 將更改字符串緩衝區,使之包含「starlet」。
在大部分狀況下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一個可變的字符序列是5.0新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用做 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種狀況很廣泛)。若是可能,建議優先採用該類,由於在大多數實現中,它比 StringBuffer 要快。二者的方法基本相同。

 

關於線程和線程不安全:java

 

概述

編輯
若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和 單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。
或者說:一個類或者程序所提供的接口對於線程來講是 原子操做或者多個線程之間的切換不會致使該接口的執行結果存在二義性,也就是說咱們不用考慮同步的問題。
線程安全問題都是由 全局變量靜態變量引發的。
若每一個線程中對全局變量、靜態變量只有讀操做,而無寫操做,通常來講,這個全局變量是線程安全的;如有多個線程同時執行寫操做,通常都須要考慮 線程同步,不然的話就可能影響線程安全。

安全性

編輯
類要成爲線程安全的,首先必須在 單線程環境中有正確的行爲。若是一個類實現正確(這是說它符合規格說明的另外一種方式),那麼沒有一種對這個類的對象的操做序列(讀或者寫公共字段以及調用公共方法)可讓對象處於無效狀態,觀察到對象處於無效狀態、或者違反類的任何不可變量、前置條件或者後置條件的狀況。
此外,一個類要 成爲線程安全的,在被多個線程訪問時,無論運行時環境執行這些線程有什麼樣的時序安排或者交錯,它必須仍然有如上所述的正確行爲,而且在調用的代碼中沒有任何額外的同步。其效果就是,在全部線程看來,對於線程安全對象的操做是以固定的、全局一致的順序發生的。
正確性與 線程安全性之間的關係很是相似於在描述 ACID(原子性、一致性、獨立性和持久性) 事務時使用的一致性與獨立性之間的關係:從特定線程的角度看,由不一樣線程所執行的對象操做是前後(雖然順序不定)而不是 並行執行的。

舉例

編輯
好比一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
單線程運行的狀況下,若是 Size = 0,添加一個元素後,此元素在位置 0,並且 Size=1;
而若是是在多線程狀況下,好比有兩個線程,線程 A 先將元素存放在位置 0。可是此時 CPU 調度線程A暫停,線程 B 獲得運行的機會。線程B也向此 ArrayList 添加元素,由於此時 Size 仍然等於 0 (注意哦,咱們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),因此線程B也將元素存放在位置0。而後線程A和線程B都繼續運行,都增長 Size 的值。
那好,咱們來看看 ArrayList 的狀況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是「線程不安全」了。

安全程度

編輯
線程安全性不是一個非真即假的命題。 Vector 的方法都是同步的,而且 Vector 明確地設計爲在多線程環境中工做。可是它的線程安全性是有限制的,即在某些方法之間有狀態依賴(相似地,若是在迭代過程當中 Vector 被其餘線程修改,那麼由 Vector.iterator() 返回的 iterator會拋出ConcurrentModificationException)。
對於 Java 類中常見的線程安全性級別,沒有一種 分類系統可被普遍接受,不太重要的是在編寫類時儘可能記錄下它們的線程安全行爲。
Bloch 給出了描述五類線程安全性的分類方法:不可變、線程安全、有條件線程安全、線程兼容和線程對立。只要明確地記錄下線程安全特性,那麼您是否使用這種系統都不要緊。這種系統有其侷限性 -- 各種之間的界線不是百分之百地明確,並且有些狀況它沒照顧到 -- 可是這套系統是一個很好的起點。這種分類系統的核心是調用者是否能夠或者必須用外部同步包圍操做(或者一系列操做)。下面幾節分別描述了 線程安全性的這五種類別。

不可變

不可變的對象必定是線程安全的,而且永遠也不須要額外的同步 [1]  。由於一個不可變的對象只要構建正確,其外部可見狀態永遠也不會改變,永遠也不會看到它處於不一致的狀態。Java 類庫中大多數基本數值類如 Integer 、 String 和 BigInteger 都是不可變的。
須要注意的是,對於Integer,該類不提供add方法,加法是使用+來直接操做。而+操做是不具線程安全的。這是提供原子操做類AtomicInteger的緣由。

線程安全

線程安全的對象具備在上面「線程安全」一節中描述的屬性 -- 由類的規格說明所規定的約束在對象被多個線程訪問時仍然有效,無論運行時環境如何排列,線程都不須要任何額外的同步。這種 線程安全性保證是很嚴格的 -- 許多類,如 Hashtable 或者 Vector 都不能知足這種嚴格的定義。

有條件的

有條件的線程安全類對於單獨的操做能夠是線程安全的,可是某些操做序列可能須要外部同步。條件線程安全的最多見的例子是遍歷由 Hashtable 或者 Vector 或者返回的 迭代器 -- 由這些類返回的 fail-fast 迭代器假定在迭代器進行遍歷的時候底層集合不會有變化。爲了保證其餘線程不會在遍歷的時候改變集合,進行迭代的線程應該確保它是獨佔性地訪問集合以實現遍歷的完整性。一般,獨佔性的訪問是由對鎖的同步保證的 -- 而且類的文檔應該說明是哪一個鎖(一般是對象的內部監視器(intrinsic monitor))。
若是對一個有條件線程安全類進行記錄,那麼您應該不只要記錄它是有條件線程安全的,並且還要記錄必須防止哪些操做序列的併發訪問。用戶能夠合理地假設其餘操做序列不須要任何額外的同步。

線程兼容

線程兼容類不是線程安全的,可是能夠經過正確使用同步而在併發環境中安全地使用。這可能意味着用一個 synchronized 塊包圍每個方法調用,或者建立一個包裝器對象,其中每個方法都是同步的(就像 Collections.synchronizedList() 同樣)。也可能意味着用 synchronized 塊包圍某些操做序列。爲了最大程度地利用線程兼容類,若是全部調用都使用同一個塊,那麼就不該該要求調用者對該塊同步。這樣作會使線程兼容的對象做爲變量實例包含在其餘線程安全的對象中,從而能夠利用其全部者對象的同步。
許多常見的類是線程兼容的,如集合類 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 類 Connection 和 ResultSet 。

線程對立

線程對立類是那些不論是否調用了外部同步都不能在併發使用時安全地呈現的類。線程對立不多見,當類修改靜態數據,而靜態數據會影響在其餘線程中執行的其餘類的行爲,這時一般會出現線程對立。線程對立類的一個例子是調用 System.setOut() 的類。
相關文章
相關標籤/搜索