Android 线程本地变量<一> ThreadLocal源码解析

来源:互联网 发布:游戏原画网络班 编辑:程序博客网 时间:2024/06/05 16:50

Android 线程本地变量<一> ThreadLocal源码解析

@(Android系统源码解析)[Android, ThreadLocal]

声明:转载请注明出处,知识有限,如有错误,请多多交流指正!

  • Android 线程本地变量一 ThreadLocal源码解析
      • ThreadLocal定义和作用
      • ThreadLocal代码使用
      • ThreadLocal源码解析

注:基于Android 6.0(API 23)源码

为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象

ThreadLocal定义和作用

1. 什么是ThreadLocal ?

JDK 1.6文档是这样定义的:

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联

每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

2. ThreadLocal的作用
每个线程都有自己的局部变量,一个线程的本地变量对其他线程是不可见的,ThreadLocal 不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制

ThreadLocal代码使用

同时开辟四个线程,操作一个数据

Map存储的时候,线程并发访问,数据都不同

    private static Map<Integer, Integer> map = new HashMap<>();    private static void test2() {        for (int i = 0; i < 4; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int n = 0;                    if (map.get(0) != null) {                        n = map.get(0);                    }                    for (int j = 0; j < 5; j++) {                        n++;                    }                    map.put(0, n);                    System.out.println(Thread.currentThread().getName() + "-->" + map.get(0));                }            }, "Thread-Map-" + i).start();        }    }

运行结果

Thread-Map-1–>5
Thread-Map-2–>10
Thread-Map-0–>15
Thread-Map-3–>20

ThreadLocal存储数据,每个数据都是独立的,线程之间没有任何干扰

 private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();    private static void test() {        for (int i = 0; i < 4; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int n = 0;                    if (threadLocal.get() != null) {                        n = threadLocal.get();                    }                    for (int j = 0; j < 5; j++) {                        n++;                    }                    threadLocal.set(n);                    System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get());                }            }, "Thread-" + i).start();        }    }

运行结果

Thread-1–>5
Thread-0–>5
Thread-2–>5
Thread-3–>5

那么ThreadLocal是如何关联线程的呢?可以看一看源码

ThreadLocal源码解析

注意:在Android中,ThreadLocal像是对原来的Java中的ThreadLcal做了优化的实现

1. ThreadLocal的结构
ThreadLocal

可以直观地看到在android中ThreadLocal类提供了一些方法和一个静态内部类Values,其中Values主要是用来保存线程的变量的一个类,它相当于一个容器,存储保存进来的变量

2. ThreadLocal的内部实现
成员变量

    /** Weak reference to this thread local instance. */    private final Reference<ThreadLocal<T>> reference= new WeakReference<ThreadLocal<T>>(this);

通过弱引用存储ThreadLocal本身,主要是防止线程自身所带的数据都无法释放,避免OOM

private static AtomicInteger hashCounter = new AtomicInteger(0);private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

hashCounter是线程安全的加减操作,getAndSet(int newValue)取当前的值,并设置新的值;而0x61c88647 * 2作用是:Value存在数据的主要存储数组table上,而table被设计为下标为0,2,4…2n的位置存放key,而1,3,5…(2n +1 )的位置存放value,`0x61c88647 * 2保证了其二进制中最低位为0,也就是在计算key的下标时,一定是偶数位


方法
- public T get():返回此线程局部变量的当前线程副本中的值

    public T get() {        // Optimized for the fast path.        // 获取当前线程        Thread currentThread = Thread.currentThread();        // 获取当前线程的Value实例        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            // 如果键值的key的索引为index,则所对应到的value索引为index+1.             // 由此分析可知 hash&values.mask 获取的就是key的索引值            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            // 如果当前Value实例为空,则创建一个Value实例            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }

可以看出get()是通过value.table这个数组通过索引值来找到值得,

initializeValues(currentThread)主要是直接new出一个新的Values对象

   Values initializeValues(Thread current) {        return current.localValues = new Values();    }
  • protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。
  protected T initialValue() {        return null;    }

也就是默认值为Null,当没有设置数据的时候,调用get()的时候,就返回Null;可以在创建ThreadLocal的时候复写initialValue()方法可以定义初始值

  • public void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值.
    public void set(T value) {         // 获取当前线程        Thread currentThread = Thread.currentThread();        // 获取当前线程的Value实例        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        // 将数据设置到Value中        values.put(this, value);    }
  • public void remove() 移除此线程局部变量当前线程的值。
  • Values values(Thread current) 通过线程获取Values对象。

Values内部存储数据,请看Android 线程本地变量<二> ThreadLocal Values源码解析博客

0 0
原创粉丝点击