1. 程式人生 > >2015我想和Java聊聊之StringBuffer是如何保證執行緒安全的

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。