1. 程式人生 > >另一鮮為人知的單例寫法-ThreadLocal

另一鮮為人知的單例寫法-ThreadLocal

共享 pos 實現 path term reat sso ddc android

另一鮮為人知的單例寫法-ThreadLocal

源代碼範例

當我閱讀FocusFinder和Choreographer的時候,我發現這兩類的單例實現和我們尋經常使用雙重檢查鎖非常不一樣。而是用來一個ThreadLocal。這個也能夠實現單例啊,那這個與雙重檢查鎖實現的單例有什麽差別呢?

1.FocusFinder

/**
 * The algorithm used for finding the next focusable view in a given direction
 * from a view that currently has focus.
 */
public class FocusFinder { private static final ThreadLocal<FocusFinder> tlFocusFinder = new ThreadLocal<FocusFinder>() { @Override protected FocusFinder initialValue() { return new FocusFinder(); } }; /** * Get the focus finder for this thread. */
public static FocusFinder getInstance() { return tlFocusFinder.get(); } // enforce thread local access private FocusFinder() {} }

2.Choreographer

public final class Choreographer {
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new
ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } return new Choreographer(looper); } }; private Choreographer(Looper looper) { mLooper = looper; mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ?

new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } } /** * Gets the choreographer for the calling thread. Must be called from * a thread that already has a [email protected] android.os.Looper} associated with it. * * @return The choreographer for this thread. * @throws IllegalStateException if the thread does not have a looper. */ public static Choreographer getInstance() { return sThreadInstance.get(); } }

理論分析

ThreadLocal會為每個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。

對於多線程資源共享的問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而後者為每個線程都提供了一份變量。因此能夠同一時候訪問而互不影響。

public class ThreadLocal{

    /**
     * Provides the initial value of this variable for the current thread.
     * The default implementation returns [email protected] null}.
     *
     * @return the initial value of the variable.
     */
    protected T initialValue() {
        return null;
    }

    /**
     * Returns the value of this variable for the current thread. If an entry
     * doesn‘t yet exist for this variable on this thread, this method will
     * create an entry, populating the value with the result of
     * [email protected] #initialValue()}.
     *
     * @return the current value of the variable for the calling thread.
     */
    @SuppressWarnings("unchecked")
    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

    /**
     * Gets Values instance for this thread and variable type.
     */
    Values values(Thread current) {
        return current.localValues;
    }

    /**
     * Sets the value of this variable for the current thread. If set to
     * [email protected] null}, the value will be set to null and the underlying entry will
     * still be present.
     *
     * @param value the new value of the variable for the caller thread.
     */
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

}

實現步驟

//1.initialValue,創建ThreadLocal對象
//2.get(),獲取當前線程裏的values
//3.假設不存在則初始化一個空的values
//4.假設存在,則復用values

另一處經典應用

在Looper中使用ThreadLocal,使之每個Thread都有一個Looper與之相應.

public class Looper{
    // sThreadLocal.get() will return null unless you‘ve called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    /** Initialize the current thread as a looper.
     * This gives you a chance to create handlers that then reference
     * this looper, before actually starting the loop. Be sure to call
     * [email protected] #loop()} after calling this method, and end it by calling
     * [email protected] #quit()}.
     */
    public static void prepare() {
       prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
       if (sThreadLocal.get() != null) {
           throw new RuntimeException("Only one Looper may be created per thread");
       }
       sThreadLocal.set(new Looper(quitAllowed));
    } 
    /**
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
    public static @Nullable Looper myLooper() {
       return sThreadLocal.get();
    }
}

自己也寫

public class Manager {

    private static final ThreadLocal<Manager> sManager = new ThreadLocal<Manager>() {
        @Override
        protected Manager initialValue() {
            return new Manager();
        }
    };

    private Manager() {

    }

    public static Manager getInstance() {
        return sManager.get();
    }
}

參考

  • 徹底理解ThreadLocal(http://blog.csdn.net/lufeng20/article/details/24314381)

另一鮮為人知的單例寫法-ThreadLocal