2015我想和Java聊聊之StringBuffer是如何保證執行緒安全的
經常有關String,StringBuffer,StringBuilder之間比較的論調出現,結論通常是單執行緒情況下StringBuilder效能優於StringBuffer,但StringBuilder是執行緒不安全的,所以多執行緒併發程式設計時候要用StringBuffer。執行緒安全就是說多執行緒訪問同一程式碼,不會產生不確定的結果。編寫執行緒安全的程式碼是低靠執行緒同步。通常情況下,執行緒不安全的類效率高,速度更快 。那麼,StringBuffer是如何保證執行緒安全的呢?
來吧,不道聽途說,翻一翻原始碼。
* @author Arthur van Hoff
* @see java.lang.StringBuilder
* @see java.lang.String
* @since JDK1.0
*/
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence
* @author Michael McCloskey
* @see java.lang.StringBuffer
* @see java.lang.String
* @since 1.5
*/
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence
這裡可見StringBuffer和StringBuilder來源出處是一致的,繼承相同的類,實現相同的介面。這裡可以看出StringBuffer從JDK1.0開始就有,StringBuilder從JDK1.5才出現。像大家所知道的那樣,StringBuilder是為了提升StringBuffer效率而出現的。
StringBuffer的構造方法:
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuffer(int capacity) {
super(capacity);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* <code>16</code> plus the length of the string argument.
*
* @param str the initial contents of the buffer.
* @exception NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
/**
* Constructs a string buffer that contains the same characters
* as the specified <code>CharSequence</code>. The initial capacity of
* the string buffer is <code>16</code> plus the length of the
* <code>CharSequence</code> argument.
* <p>
* If the length of the specified <code>CharSequence</code> is
* less than or equal to zero, then an empty buffer of capacity
* <code>16</code> is returned.
*
* @param seq the sequence to copy.
* @exception NullPointerException if <code>seq</code> is <code>null</code>
* @since 1.5
*/
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
StringBuilder的構造方法:
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the <code>capacity</code> argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* <code>16</code> plus the length of the string argument.
*
* @param str the initial contents of the buffer.
* @throws NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
/**
* Constructs a string builder that contains the same characters
* as the specified <code>CharSequence</code>. The initial capacity of
* the string builder is <code>16</code> plus the length of the
* <code>CharSequence</code> argument.
*
* @param seq the sequence to copy.
* @throws NullPointerException if <code>seq</code> is <code>null</code>
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
所有構造方法都一致。
public synchronized int length() {
return count;
}
public synchronized int capacity() {
return value.length;
}
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
StringBuilder沒有重寫length()和capacity()等方法,StringBuffer給方法添加了同步鎖,從這裡開始,StringBuffer和StringBuilder出現區別。StringBuffer把所有操作類的方法均新增同步鎖,而StringBuilder並沒有。
StringBuffer的append方法:
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
StringBuilder的append方法:
private StringBuilder append(StringBuilder sb) {
if (sb == null)
return append("null");
int len = sb.length();
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
sb.getChars(0, len, value, count);
count = newcount;
return this;
}
這裡append,作為StringBuffer/StringBuilder最常用的方法之一,區別已經很明顯了。(這裡也可以看到,StringBuffer的很多方法是在JDK1.5版本的時候才新增的)
檢視synchronized的描述吧:
synchronized 方法控制對類成員變數的訪問:每個類例項對應一把鎖,每個 synchronized 方法都必須 獲得呼叫該方法的類例項的鎖方能執行,否則所屬執行緒阻塞,方法一旦執行,就獨佔該鎖,直到從該方法 返回時才將鎖釋放,此後被阻塞的執行緒方能獲得該鎖,重新進入可執行狀態。
所以,StringBuffer不可避免的比StringBuilder慢。在不需要考慮執行緒安全的程式碼,儘量還是使用StringBuilder吧。順便,HashTable和HashMap之間的關係也是這樣的。HashTable原始碼裡,也充斥著synchronized。