• 另一鲜为人知的单例写法-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会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的訪问冲突。

    对于多线程资源共享的问题,同步机制採用了“以时间换空间”的方式,而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();
        }
    }

    參考

  • 相关阅读:
    lambda表达式——sort和compare进行高级排序
    guava包的应用总结
    ffmpeg源码编译安装(Compile ffmpeg with source) Part 1 : 通用部分
    Notes : <Hands-on ML with Sklearn & TF> Chapter 4
    Notes : <Hands-on ML with Sklearn & TF> Chapter 3
    scxml-1
    redis
    Async Actions using Coroutines(异步行为使用协程,协同程序)
    Pass additional arguments during initialization and/or construction
    Wraping legacy code
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7365403.html
Copyright © 2020-2023  润新知