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

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

@(Android系统源码解析)[Android, 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值