另一鮮為人知的單例寫法-ThreadLocal
另一鮮為人知的單例寫法-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