另一鲜为人知的单例写法-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>() {@Overrideprotected FocusFinder initialValue() {return new FocusFinder();}};/*** Get the focus finder for this thread.*/public static FocusFinder getInstance() {return tlFocusFinder.get();}// enforce thread local accessprivate FocusFinder() {}
}
2.Choreographer
public final class Choreographer {// Thread local storage for the choreographer.private static final ThreadLocal<Choreographer> sThreadInstance =new ThreadLocal<Choreographer>() {@Overrideprotected 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>() {@Overrideprotected Manager initialValue() {return new Manager();}};private Manager() {}public static Manager getInstance() {return sManager.get();}
}
參考
- 彻底理解ThreadLocal(http://blog.csdn.net/lufeng20/article/details/24314381)
另一鲜为人知的单例写法-ThreadLocal相关推荐
- 单例设计模式-ThreadLocal线程单例
package com.learn.design.pattern.creational.singleton;/*** 是基于ThreadLocal的* 所以必不可少要使用这个类* * 我们看一下这个类 ...
- iOS严谨单例写法/可继承单例
单例模式在iOS开发中可能算是最常用的模式之一了,但是由于OC本身的语言特性,想要写一个正确的单例模式相对来说比较麻烦. 今天就来说一说, 单例创建的方式和严谨的单例写法及可继承单例编写. 基本单例的 ...
- java单例弊端,博客大牛总结的Java单例写法的优缺点及使用场景
在java中,单例有很多种写法,面试时,手写代码环节,除了写算法题,有时候也会让手写单例模式,这里记录一下单例的几种写法和优缺点. 为什么使用单例 1.在内存中只有一个对象,节省内存空间.避免频繁的创 ...
- java 单例写法_java 单例模式的几种写法
一.懒汉式 public classSingleton{private static Singleton instance = null;privateSingleton(){}public stat ...
- 没想到,错误的单例写法,让 RabbitMQ 大量超时导致程序挂死!
一:背景 1. 讲故事 10月份星球里的一位老朋友找到我,说他们公司的程序在一个网红直播带货下给弄得无响应了,无响应期间有大量的 RabbitMQ 超时,寻求如何找到根源,聊天截图我就不发了. 既然无 ...
- filter java 是单例的吗_JAVA 设计模式之 单例模式详解
单例模式:(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点.单例模式是创建型模式.单例模式在现实生活中应用也非常广泛. 在 J2EE 标准中,S ...
- 单例设计模式详解。。。。。。。。。。。
public class Demo01 { public static void main(String[] args) { // TODO Auto-generated method stub /* ...
- @Singleton能保证单例吗
scope里有个@Singleton,它能保证单例吗? 答案是不完全能(或者是说是有条件的能) 当你不使用@Singleton时,在同一个宿主类里,注入两次同一个类的对象你会发现,两个对象的地址不一样 ...
- 游戏设计模式——C++单例类
前言: 本文将探讨单例类设计模式,单例类的懒汉模式/饿汉模式,单例类的多线程安全性,最后将利用C++模板减少单例类代码量. 本文假设有一个Manager管理类,并以此为探究单例类的设计模式. 懒汉模式 ...
最新文章
- 正则表达式简介及在C++11中的简单使用
- 等差数列末项_等差数列末项(第n项)公式
- Hyper-V 3.0实用技巧:创建虚拟机组快照
- 如何在bash脚本中提示用户进行确认? [重复]
- mysql查询根据季度查询_Mysql 按年、季度、月查询统计
- 避免App沦为“僵尸”的12个秘诀
- 使用脚本编写 Vim 编辑器,第 4 部分: 字典
- matlab版本之间的单双引号问题
- 用set和shopt设置bash选项
- winpcap基本原理及常见应用_数字图像处理原理及应用教学大纲
- maven 插件之maven-enforcer-plugin的使用
- c语言dfs算法全排列代码,c语言dfs解决全排列问题
- NOI图论算法:网络流
- 安装pkgconfig_一个R包怎么也安装不上,憋着急!
- html中多个div分开排列,CSS+DIV设计实例:多个DIV排列时居中
- CCNA学习指南第十章
- Verilog——38译码器(包括仿真文件和约束文件的格式)
- dell 恢复介质_戴尔介质恢复选项
- 如何在CentOS 7上安装Elasticsearch,Logstash和Kibana(ELK堆栈)
- 《区块链基础知识25讲》-第七讲-双花问题
热门文章
- UA MATH563 概率论的数学基础1 概率空间2 可列状态空间
- UA MATH566 统计理论 Cramer-Rao不等式与Delta方法的联系
- UA MATH566 统计理论7: Multiple Test
- Bochs调试Linux内核6 - 启动过程调试 - 跳到bootsect引导程序执行
- 初次使用VS附加到进程功能
- Matlab冒号操作符图解
- 并发编程之Synchronized原理
- Android上下左右滑动,显示底层布局
- autocad.net中ResultBuffer相关的常量值
- html贪吃蛇自动走,分享一个用html5实现的贪吃蛇特效代码