还有一鲜为人知的单例写法-ThreadLocal

Posted ihrthk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了还有一鲜为人知的单例写法-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 @link 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会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
对于多线程资源共享的问题?
1.同步机制采用了“以时间换空间”的方式,提供一份变量,
让不同的线程排队访问。
2.ThreadLocal采用了“以空间换时间”的方式,为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

public class ThreadLocal
    
    /**
     * Provides the initial value of this variable for the current thread.
     * The default implementation returns @code 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
     * @link #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
     * @code 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
     * @link #loop() after calling this method, and end it by calling
     * @link #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的主要内容,如果未能解决你的问题,请参考以下文章

有一鲜为人知的单例写法-ThreadLocal

另一鲜为人知的单例写法-ThreadLocal

性能比较好的单例写法

一日一技:Python 下面最简单的单例模式写法

简单的单例模式其实也不简单

基于unity的单例设计模式写法