您的位置:首页 > 移动开发 > Android开发

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

2016-02-29 17:10 661 查看

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
存储的时候,线程并发访问,数据都不同

[code]    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
存储数据,每个数据都是独立的,线程之间没有任何干扰

[code] 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的结构



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

2. ThreadLocal的内部实现

成员变量

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


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

[code]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():返回此线程局部变量的当前线程副本中的值

[code]    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
对象

[code]   Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }


protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。

[code]  protected T initialValue() {
        return null;
    }


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

public void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值.

[code]    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源码解析
博客
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: