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

源代码范例

当我阅读FocusFinder和Choreographer的时候,我发现这两类的单例实现和我们寻经常使用双重检查锁非常不一样。而是用来一个ThreadLocal。这个也能够实现单例啊,那这个与双重检查锁实现的单例有什么差别呢?

1.FocusFinder

  1. /**
  2. * The algorithm used for finding the next focusable view in a given direction
  3. * from a view that currently has focus.
  4. */
  5. public class FocusFinder {
  6. private static final ThreadLocal<FocusFinder> tlFocusFinder =
  7. new ThreadLocal<FocusFinder>() {
  8. @Override
  9. protected FocusFinder initialValue() {
  10. return new FocusFinder();
  11. }
  12. };
  13. /**
  14. * Get the focus finder for this thread.
  15. */
  16. public static FocusFinder getInstance() {
  17. return tlFocusFinder.get();
  18. }
  19. // enforce thread local access
  20. private FocusFinder() {}
  21. }

2.Choreographer

  1. public final class Choreographer {
  2. // Thread local storage for the choreographer.
  3. private static final ThreadLocal<Choreographer> sThreadInstance =
  4. new ThreadLocal<Choreographer>() {
  5. @Override
  6. protected Choreographer initialValue() {
  7. Looper looper = Looper.myLooper();
  8. if (looper == null) {
  9. throw new IllegalStateException("The current thread must have a looper!");
  10. }
  11. return new Choreographer(looper);
  12. }
  13. };
  14. private Choreographer(Looper looper) {
  15. mLooper = looper;
  16. mHandler = new FrameHandler(looper);
  17. mDisplayEventReceiver = USE_VSYNC ?
  18. new FrameDisplayEventReceiver(looper) : null;
  19. mLastFrameTimeNanos = Long.MIN_VALUE;
  20. mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
  21. mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
  22. for (int i = 0; i <= CALLBACK_LAST; i++) {
  23. mCallbackQueues[i] = new CallbackQueue();
  24. }
  25. }
  26. /**
  27. * Gets the choreographer for the calling thread. Must be called from
  28. * a thread that already has a {@link android.os.Looper} associated with it.
  29. *
  30. * @return The choreographer for this thread.
  31. * @throws IllegalStateException if the thread does not have a looper.
  32. */
  33. public static Choreographer getInstance() {
  34. return sThreadInstance.get();
  35. }
  36. }

理论分析

ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的訪问冲突。

对于多线程资源共享的问题,同步机制採用了“以时间换空间”的方式,而ThreadLocal採用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队訪问,而后者为每个线程都提供了一份变量。因此能够同一时候訪问而互不影响。

  1. public class ThreadLocal{
  2. /**
  3. * Provides the initial value of this variable for the current thread.
  4. * The default implementation returns {@code null}.
  5. *
  6. * @return the initial value of the variable.
  7. */
  8. protected T initialValue() {
  9. return null;
  10. }
  11. /**
  12. * Returns the value of this variable for the current thread. If an entry
  13. * doesn't yet exist for this variable on this thread, this method will
  14. * create an entry, populating the value with the result of
  15. * {@link #initialValue()}.
  16. *
  17. * @return the current value of the variable for the calling thread.
  18. */
  19. @SuppressWarnings("unchecked")
  20. public T get() {
  21. // Optimized for the fast path.
  22. Thread currentThread = Thread.currentThread();
  23. Values values = values(currentThread);
  24. if (values != null) {
  25. Object[] table = values.table;
  26. int index = hash & values.mask;
  27. if (this.reference == table[index]) {
  28. return (T) table[index + 1];
  29. }
  30. } else {
  31. values = initializeValues(currentThread);
  32. }
  33. return (T) values.getAfterMiss(this);
  34. }
  35. /**
  36. * Gets Values instance for this thread and variable type.
  37. */
  38. Values values(Thread current) {
  39. return current.localValues;
  40. }
  41. /**
  42. * Sets the value of this variable for the current thread. If set to
  43. * {@code null}, the value will be set to null and the underlying entry will
  44. * still be present.
  45. *
  46. * @param value the new value of the variable for the caller thread.
  47. */
  48. public void set(T value) {
  49. Thread currentThread = Thread.currentThread();
  50. Values values = values(currentThread);
  51. if (values == null) {
  52. values = initializeValues(currentThread);
  53. }
  54. values.put(this, value);
  55. }
  56. }

实现步骤

  1. //1.initialValue,创建ThreadLocal对象
  2. //2.get(),获取当前线程里的values
  3. //3.假设不存在则初始化一个空的values
  4. //4.假设存在,则复用values

另一处经典应用

在Looper中使用ThreadLocal,使之每个Thread都有一个Looper与之相应.

  1. public class Looper{
  2. // sThreadLocal.get() will return null unless you've called prepare().
  3. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  4. /** Initialize the current thread as a looper.
  5. * This gives you a chance to create handlers that then reference
  6. * this looper, before actually starting the loop. Be sure to call
  7. * {@link #loop()} after calling this method, and end it by calling
  8. * {@link #quit()}.
  9. */
  10. public static void prepare() {
  11. prepare(true);
  12. }
  13. private static void prepare(boolean quitAllowed) {
  14. if (sThreadLocal.get() != null) {
  15. throw new RuntimeException("Only one Looper may be created per thread");
  16. }
  17. sThreadLocal.set(new Looper(quitAllowed));
  18. }
  19. /**
  20. * Return the Looper object associated with the current thread. Returns
  21. * null if the calling thread is not associated with a Looper.
  22. */
  23. public static @Nullable Looper myLooper() {
  24. return sThreadLocal.get();
  25. }
  26. }

自己也写

  1. public class Manager {
  2. private static final ThreadLocal<Manager> sManager = new ThreadLocal<Manager>() {
  3. @Override
  4. protected Manager initialValue() {
  5. return new Manager();
  6. }
  7. };
  8. private Manager() {
  9. }
  10. public static Manager getInstance() {
  11. return sManager.get();
  12. }
  13. }

參考

另一鲜为人知的单例写法-ThreadLocal的更多相关文章

  1. Egret中的三种单例写法

    1 普通的单例写法 as3中也是这么个写法. 缺点:每个单例类里都要写instance和getInstance. class Single{ private static instance:Singl ...

  2. Unity 单例写法

    借鉴自:http://www.cnblogs.com/CodeCabin/p/unity_global_manager.html 实现复杂一些的全局控制,如切换游戏关卡等操作,更常用的方式是使用单例类 ...

  3. 设计模式课程 设计模式精讲 8-10 单例设计模式-ThreadLocal线程单例

    1 课程讲解 1.1 应用场景 2 代码演练 2.1 threadLocal应用 1 课程讲解 1.1 应用场景 多线程的时候: 使用同步锁使用时间换空间的方式,(线程排队时间比较长) 而使用thre ...

  4. ARC模式下的单例写法

    // 单例 + (id)sharedInstance { __strong static id sharedObject = nil; static dispatch_once_t onceToken ...

  5. swift3 单例写法

    import UIKit class SingleOnce { // 单例 static let shared = SingleOnce.init() private init(){} // 其他方法 ...

  6. 基于单例使用ThreadLocal对多线程下数据的访问修改

    package cn.lyy.thread; import java.util.Random; /** * 基于单例模式的基础上,使用ThreadLocal为每一个进入的线程生成一个实例, * 用来对 ...

  7. myapplication 单例写法

    MyApplication extends Application private static MyApplication myApplication = null; oncreate中: @Ove ...

  8. C++单例写法

    #define __xx(WaveClassFile::me()) class Xx : public QObject{ Q_OBJECT public: static Xx & me(); ...

  9. 十次艳遇单例设计模式(Singleton Pattern)

    1.引言 单例设计模式(Singleton Pattern)是最简单且常见的设计模式之一,在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访 ...

随机推荐

  1. [Android Security] APK自我保护 - DEX/APK校验

    cp : https://segmentfault.com/a/1190000005105973 DEX校验 classes.dex 是 Android 虚拟机的可执行文件,我们所写的 java 代码 ...

  2. 几种常见的JavaScript混淆和反混淆工具分析实战

    几种常见的JavaScript混淆和反混淆工具分析实战 xiaix2016-03-05+8共1195751人围观 ,发现 5 个不明物体WEB安全 信息安全常被描述成一场军备竞赛,白帽与黑帽,渗透测试 ...

  3. 安装chrome

    安装chrome 在suse12中安装chrome时,提示 lsb >= 4.0 is needed by google-chrome-stable 到http://rpm.pbone.net当 ...

  4. Android - View的绘制流程一(measure)

    该博文所用的demo结构图: 相应的代码: MainActivity.java: [java] view plain copy <span style="font-family:Mic ...

  5. 第二章 BIO与NIO

    <netty权威指南>读书笔记 一.BIO 1.服务端程序: package bio; import java.io.BufferedReader; import java.io.IOEx ...

  6. High Availability (HA) 和 Disaster Recovery (DR) 的区别

    High availability 和disaster recovery不是一回事. 尽管在规划和解决方案上有重叠的部分, 它们俩都是business contiunity的子集. HA的目的是在主数 ...

  7. jquery ajax 的 $.get()用法详解

    js文件 $(document).ready(function(){ $("form").submit(function(event) {event.preventDefault( ...

  8. oauth2-server-php-docs 食谱

    一步一步的演练 以下说明提供详细的演练,以帮助您启动并运行OAuth2服务器.要查看实现此库的现有OAuth2服务器的代码库,请查看OAuth2 Demo. 初始化您的项目 为您的项目创建一个目录,并 ...

  9. 海马模拟器连不上ADB的解决方法

    http://yunpan.cn/c3xMeYhvVsEIq  访问密码 fa8d先暂时用这个工具吧,官方提供的,不过目前不是最终版,后面会加入到模拟器中 adb connect 127.0.0.1: ...

  10. LNMP(Linux+Nginx+MySQL+PHP)centos6.4安装

    nginx命令 停止nginx服务:# /etc/init.d/nginx stop 启动nginx服务:# /etc/init.d/nginx start 编辑nginx配置文件:# vim /et ...