android线程管理三(ThreadLocal)
来源:互联网 发布:亲淘软件下载 编辑:程序博客网 时间:2024/06/11 20:54
前言
本篇主要讨论一下ThreadLocal,转载请注明出处:小石头的博客 http://blog.csdn.net/lu1024188315/article/details/74518599
一 什么是ThreadLocal
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
二 ThreadLocal内部成员
接下来,来看下ThreadLocal内部的几个成员。
1 ThreadLocalMap
在《Thread》这篇文章中都已经提到过,在对Thread初始化的时候,会为Thread创建一个ThreadLocalMap,如下:
Thread#int2:
private void init2(Thread parent) { //获取线程parent的类加载器 ClassLoader类型 this.contextClassLoader = parent.getContextClassLoader(); this.inheritedAccessControlContext = AccessController.getContext(); if (parent.inheritableThreadLocals != null) { //给当前线程创建ThreadLocalMap对象,并且继承parent的ThreadLocalMap中的数据 this.inheritableThreadLocals = ThreadLocal.createInheritedMap( parent.inheritableThreadLocals); }
}
那么ThreadLocalMap是什么?其部分源码如下:
ThreadLocal#ThreadLocalMap:
static class ThreadLocalMap { ....... static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } .......}
说明,ThreadLocalMap是ThreadLocal的内部类继承了弱引用,从上述代码可以出其结构是key-value结构,key存储的是ThreadLocal类型,value是Object类型。完成可以把它看作是一个Map,用来保存变量的副本。
2 get函数分析
ThreadLocal#get:
public T get() { //调用native层函数获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) { //获取此线程局部变量的当前线程副本中(线程本地变量)的值,如果这是线程第一次调用该方法, //则调用setInitialValue函数创建并初始化此副本 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue();}
3 set函数分析
ThreadLocal#set:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //设置局部变量在当前线程中的副本的值,如果map为null,则创建它,并初始化副本 if (map != null) map.set(this, value); else createMap(t, value);}
4 initialValue函数分析
ThreadLocal#initialValue:
protected T initialValue() { return null;}
说明,这个函数顾名思义就是用来初始化线程局部变量,尽管这里没有体现,但它其实在setInitialValue中体现到了,ThreadLocal#setInitialValue 如下:
private T setInitialValue() { //获取刚刚初始化的值并保存到map中 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value;}
5 remove函数分析
ThreadLocal#remove:
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this);}
说明,这个函数移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。
三 ThreadLocal功能体现
说了这么多,也许你对ThreadLocal的功能还是比较模糊,那么就来一个简单的实例来具体体现一下,这里只是部分代码,完整代码点击查看。
首先声明两个变量一个使用ThreadLocal,另一个不用ThreadLocal,并给它们初始化:
/**普通数据*/ private int num = 0; /** 经过ThreadLocal处理的数据*/ private ThreadLocal<Integer> threadLocalNum = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { //初始值为0 return 0; } };
再者建立两个子线程,并改变它们的值进行打印:
new Thread(new Runnable() { @Override public void run() { num += 1; threadLocalNum.set(threadLocalNum.get() + 1); Log.e("线程1===num=====》","" + num);// 线程1===num=====》: 1 Log.e("线程1====threadLocal==》","" + threadLocalNum.get());// 线程1====threadLocal==》: 1 } }).start();new Thread(new Runnable() { @Override public void run() { ThreadlocalTestActivity.this.runOnUiThread(new Runnable() { @Override public void run() { num += 1; threadLocalNum.set(threadLocalNum.get() + 1); Log.e("线程2========》","" + num);// 线程2========》: 2 Log.e("线程2====threadLocal==》","" + threadLocalNum.get()); //线程2====threadLocal==》: 1 } }); } }).start();
说明,打印结果上面已经给出,从结果可以看出num在两个子线程中是受影响的,它的值先由0变为1再变为2,而threadLocalNum在两个子线程中,都是由0变为1,彼此之间不受影响,这个因为在两个子线程中都有一个threadLocalNum副本,当线程改变这个副本,另一个线程的副本不受影响没有做出改变。好,到这里应该对ThreadLocal有一个基本的认识了吧,那么我们继续探索。
四 ThreadLocal的实现版
其实ThreadLocal其本质就是通过Map结构的集合,把变量的副本保存到本地,然后提供get、set、remove等方法来改变这个副本,而其他线程不受影响。
public class SimpleThreadLocal{ private Map valueMap = Collections.synchronizedMap(new HashMap()); /** * 设置副本的值 * */ public void set(Object newValue) { //①键为线程对象,值为本线程的变量副本 valueMap.put(Thread.currentThread(), newValue); } /** * 获取副本的值 * */ public Object get() { Thread currentThread = Thread.currentThread(); //②返回本线程对应的变量 Object o = valueMap.get(currentThread); //③如果在Map中不存在,放到Map中保存起来。 if (o == null && !valueMap.containsKey(currentThread)) { o = initialValue(); valueMap.put(currentThread, o); } return o; } /** * 移除副本的值 * */ public void remove() { valueMap.remove(Thread.currentThread()); } /** * * */ public Object initialValue() { return null; }}
五 ThreadLocal与同步机制的比较
ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
参考文献:
1《深入研究java.lang.ThreadLocal类》
2《 彻底理解ThreadLocal》
3《理解ThreadLocal》
阅读全文
1 0
- android线程管理三(ThreadLocal)
- Android线程管理(三)
- Android线程管理之ThreadLocal理解及应用场景
- Android线程—ThreadLocal
- 本地线程管理、仿ThreadLocal
- 线程(ThreadLocal)
- ThreadLocal(线程局部变量)
- Java 并发编程(三)线程管理
- Android线程管理(二)
- Android线程管理(一)
- 【线程】 ThreadLocal
- 深入Java线程管理(三):线程同步
- 多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)
- Hibernate用ThreadLocal模式 (线程局部变量模式) 管理Session
- Android进程和线程 --消息队列模型--ThreadLocal (3)(2015-12-02 19:41)
- 线程变量本地化类ThreadLocal(1)
- 线程变量隔离化ThreadLocal(2)
- 线程变量安全化ThreadLocal(3)
- As3基础部分1
- 代码片段day13-day15
- HDU 5119 Happy Matt Friends(动态规划)【状压基础类模板】
- SpringBoot5-spring高级话题-组合注解与元注解,@Enable*注解的工作原理,测试
- sduacm2016级暑假集训 搜索&并查集
- android线程管理三(ThreadLocal)
- 曼哈顿距离与欧氏距离
- 第四篇:服务发现机制
- STC15W4K32S4读取MPU6050陀螺仪角度加速度串口显示程序代码
- poi上传报表行数不匹配
- Java学习笔记:类在继承中的多态性
- 欢迎使用CSDN-markdown编辑器
- linux历史追溯
- HTML学习笔记:第一课