[Android]ThreadLocal的定义和用途

ThreadLocal用于实现在不同的线程中存储线程私有数据的类。在多线程的环境中,当多个线程需要对某个变量进行频繁操作,同时各个线程间不需要同步,此时,各个子线程只需要对存储在当前线程中的变量的拷贝进行操作即可,程序的运行效率会很高,即所谓的空间换时间。

在开源框架EventBus和android系统的Looper类当中有运用到ThreadLocal进行线程局部数据的存储,android版的ThreadLocal和java原生的ThreadLocal有一定的差别,android版的进行了一些优化设计,通过内部类Values中的Object数组来存储ThreadLocal的弱引用和线程的局部数据对象;而java版的是以MAP的方式来存储。

[Android]ThreadLocal的基本使用

ThreadLocal的使用很简单,定义一个全局的ThreadLocal对象,在线程中通过get()方法得到当前线程的局部数据进行操作。

  1. ThreadLocal<ClassType> storageDataThreadLocal = new ThreadLocal<ClassType>(){
  2. @Override
  3. protected ClassType initialValue() {
  4. return new ClassType();
  5. }
  6. };

ClassType为自定义的数据类,实例化ThreadLocal对象过程中如果没有重写initialValue方法则第一次调用get方法会返回空,这里需要注意。

[Android]ThreadLocal实现线程数据拷贝的原理

我们关注最多的应该就是ThreadLocal如何实现线程的局部数据存储,通过分析源码可以得到最终的答案。

我们直接分析get函数:

  1. /**
  2. * Returns the value of this variable for the current thread. If an entry
  3. * doesn't yet exist for this variable on this thread, this method will
  4. * create an entry, populating the value with the result of
  5. * {@link #initialValue()}.
  6. *
  7. * @return the current value of the variable for the calling thread.
  8. */
  9. @SuppressWarnings("unchecked")
  10. public T get() {
  11. // Optimized for the fast path.
  12. //通过native方法得到代码当前执行的线程对象
  13. Thread currentThread = Thread.currentThread();
  14. //得到当前线程的Values类型对象
  15. Values values = values(currentThread);
  16. if (values != null) {
  17. //得到对象数组table,用来存储当前ThreadLocal对象的弱引用
  18. //和当前线程的局部数据对象引用
  19. Object[] table = values.table;
  20. //得到用于存储ThreadLocal弱引用的散列值索引
  21. int index = hash & values.mask;
  22. //指向到对象相同,则返回其引用
  23. if (this.reference == table[index]) {
  24. return (T) table[index + 1];
  25. }
  26. } else {
  27. //新建values对象赋给当前线程对象的localValues引用
  28. values = initializeValues(currentThread);
  29. }
  30. return (T) values.getAfterMiss(this);
  31. }

如果当前线程对象内的Values对象中有存储和当前ThreadLocal对象的reference属性是同一个对象的引用则返回此线程的本地数据,即变量在线程的本地拷贝。

否则在当前线程内部创建一个Values对象,通过Values.getAfterMiss将ThreadLocal类的initialValue方法得到的本地数据存储到当前线程对象内部并返回;initialValue是protected方法,如果子类没有重写,则默认返回空。

静态内部类Values的getAfterMiss函数:

  1. /**
  2. * Gets value for given ThreadLocal after not finding it in the first
  3. * slot.
  4. */
  5. Object getAfterMiss(ThreadLocal<?> key) {
  6. Object[] table = this.table;
  7. //通过散列算法得到ThreadLocal的first slot的索引值
  8. int index = key.hash & mask;
  9. // If the first slot is empty, the search is over.
  10. if (table[index] == null) {
  11. //如果first slot上没有存储 则将ThreadLocal的弱引用和本地数据
  12. //存储到table数组的相邻位置并返回本地数据对象引用
  13. Object value = key.initialValue();
  14. // If the table is still the same and the slot is still empty...
  15. if (this.table == table && table[index] == null) {
  16. table[index] = key.reference;
  17. table[index + 1] = value;
  18. size++;
  19. cleanUp();
  20. return value;
  21. }
  22. // The table changed during initialValue().
  23. //遍历table数组,根据不同判断将ThreadLocal的
  24. //弱引用和本地数据对象引用存储到table数组的相应位置
  25. put(key, value);
  26. return value;
  27. }
  28. // Keep track of first tombstone. That's where we want to go back
  29. // and add an entry if necessary.
  30. int firstTombstone = -1;
  31. // Continue search.
  32. for (index = next(index);; index = next(index)) {
  33. Object reference = table[index];
  34. if (reference == key.reference) {
  35. return table[index + 1];
  36. }
  37. // If no entry was found...
  38. if (reference == null) {
  39. Object value = key.initialValue();
  40. // If the table is still the same...
  41. if (this.table == table) {
  42. // If we passed a tombstone and that slot still
  43. // contains a tombstone...
  44. if (firstTombstone > -1
  45. && table[firstTombstone] == TOMBSTONE) {
  46. table[firstTombstone] = key.reference;
  47. table[firstTombstone + 1] = value;
  48. tombstones--;
  49. size++;
  50. // No need to clean up here. We aren't filling
  51. // in a null slot.
  52. return value;
  53. }
  54. // If this slot is still empty...
  55. if (table[index] == null) {
  56. table[index] = key.reference;
  57. table[index + 1] = value;
  58. size++;
  59. cleanUp();
  60. return value;
  61. }
  62. }
  63. // The table changed during initialValue().
  64. put(key, value);
  65. return value;
  66. }
  67. if (firstTombstone == -1 && reference == TOMBSTONE) {
  68. // Keep track of this tombstone so we can overwrite it.
  69. firstTombstone = index;
  70. }
  71. }
  72. }

getAfterMiss函数根据不同的判断将ThreadLocal的弱引用和当前线程的本地对象以类似MAP的方式,存储在table数组的相邻位置,其中其散列的索引hash值是通过hashCounter.getAndAdd(0x61c88647 * 2)算法来得到。

set函数:

  1. /**
  2. * Sets the value of this variable for the current thread. If set to
  3. * {@code null}, the value will be set to null and the underlying entry will
  4. * still be present.
  5. *
  6. * @param value the new value of the variable for the caller thread.
  7. */
  8. public void set(T value) {
  9. Thread currentThread = Thread.currentThread();
  10. //得到当前线程的本地Values对象
  11. Values values = values(currentThread);
  12. if (values == null) {
  13. //为空则新建Values对象
  14. values = initializeValues(currentThread);
  15. }
  16. //将当前ThreadLocal对象引用和本地数据存储到
  17. //当前ThreadLocal的内部Values对象的table数组当中
  18. //当调用get方法时,获得的即是当前线程的本地数据
  19. values.put(this, value);
  20. }

[Android]关于ThreadLocal内存泄漏的问题

在其他的博文中有说到,java版的ThreadLocal“value值因为存在一条从current thread连接过来的强引用,只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.”.

在android版的ThreadLocal中value值不存在强引用,故在table数组中的指定位置置为null,本地数据则没有存在指向其的引用,当GC回收的时候会清理掉。

[Android]ThreadLocal总结

ThreadLocal实现线程本地存储的原理是比较清晰的,即在当前线程中调用get方法时,通过ThreadLocal的initialValue方法创建当前线程的一个本地数据拷贝,将此拷贝添加到当前线程本地数据的table数组当中;或者在调用set方法时,将当前线程的本地数据存储到当前线程的table数组中.当前线程通过调用ThreadLocal对象的get方法即得到当前线程本地数据对象。

Android开发之ThreadLocal原理深入理解的更多相关文章

  1. Android开发之InstanceState详解

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  2. 【Android UI】Android开发之View的几种布局方式及实践

    引言 通过前面两篇: Android 开发之旅:又见Hello World! Android 开发之旅:深入分析布局文件&又是“Hello World!” 我们对Android应用程序运行原理 ...

  3. Android开发之InstanceState详解(转)---利用其保存Activity状态

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  4. Android开发之旅4:应用程序基础及组件

    引言 为了后面的例子做准备,本篇及接下来几篇将介绍Android应用程序的原理及术语,这些也是作为一个Android的开发人员必须要了解,且深刻理解的东西.本篇的主题如下: 1.应用程序基础 2.应用 ...

  5. Android开发之旅3:android架构

    引言 通过前面两篇: Android 开发之旅:环境搭建及HelloWorld Android 开发之旅:HelloWorld项目的目录结构 我们对android有了个大致的了解,知道如何搭建andr ...

  6. Android开发之Java必备基础

    Android开发之Java必备基础 Java类型系统 Java语言基础数据类型有两种:对象和基本类型(Primitives).Java通过强制使用静态类型来确保类型安全,要求每个变量在使用之前必须先 ...

  7. Android开发之PopupWindow

      /* *  Android开发之PopupWindow * *  Created on: 2011-8-8 *  Author: blueeagle *  Email: liujiaxiang@g ...

  8. Android 开发之旅:深入分析布局文件&又是“Hello World!”

    http://www.cnblogs.com/skynet/archive/2010/05/20/1740277.html 引言 上篇可以说是一个分水岭,它标志着我们从Android应用程序理论进入实 ...

  9. Android开发之eclipse 快捷键

    转自:<Android开发之eclipse 快捷键>http://www.cnblogs.com/aimeng/archive/2012/08/07/2626909.html Ctrl+1 ...

随机推荐

  1. [luoguP1586] 四方定理(DP 背包)

    传送门 相当于背包, f[i][j] 表示当前数为 i,能分解成 j 个数的平方的和的数量 那么就是统计背包装物品的数量 ——代码 #include <cmath> #include &l ...

  2. 分享一个灰常好的 dapper 扩展插件: Rainbow

    dapper 是一个效率非常高的orm  框架 ,效率要远远大于 我们大微软的EF .    它只有一个类文件,非常之小.(在 EF 5.0 后 微软已经做了 改进) ps; 由于之前我也没测试过,只 ...

  3. MySQL慢日志切割邮件发送脚本

    #!/bin/bashtime=`date -d yesterday +"%Y-%m-%d"`slowlog='/usr/local/percona/data/slow.log'# ...

  4. BZOJ(8) 1053: [HAOI2007]反素数ant

    1053: [HAOI2007]反素数ant Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4118  Solved: 2453[Submit][St ...

  5. Ubuntu中PPA源是什么

    以下内容转自https://imcn.me/ppa: PPA是Personal Package Archives首字母简写.翻译为中文意思是:个人软件包文档 只有Ubuntu用户可以用,而所有的PPA ...

  6. SiteMesh2-decorators.xml文件

    SiteMesh默认使用decorators.xml作为装饰配置文件. decorators.xml顶层元素概览如下: <decorators> <decorator/> &l ...

  7. LintCode-分糖果

    有 N 个小孩站成一列.每一个小孩有一个评级. 依照下面要求.给小孩分糖果: 每一个小孩至少得到一颗糖果. 评级越高的小孩能够得到很多其它的糖果. 需最少准备多少糖果? 您在真实的面试中是否遇到过这个 ...

  8. C++与C的那些差异

    虽说C++是向后兼容C的,但C++与C还是存在许多差异.这里举了几个例子,也是我们很容易忽略的地方.如果你还知道其他的更多的Differences,或者发现什么错误,可以评论告诉我,大家一起学习进步. ...

  9. C#根据规则生成6位随机码

    #region 获得6位优惠码 zhy public static string CreatePromoCode(string code) { if (code == "") { ...

  10. 模板小程序】求小于等于N范围内的质数

    xiaoxi666 联系邮箱: xiaoxi666swap@163.com 博客园 首页 新随笔 联系 订阅 管理 [模板小程序]求小于等于N范围内的质数   1 //筛法求N以内的素数(普通法+优化 ...