ThreadLocal的原理

1. ThreadLocal是什么?

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。其实,ThreadLocal是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,ThreadLocal就像是线程的本地变量,这也是类名中“Local”所要表达的意思。

2. ThreadLocal的用法

public class Test {
    public static void main(String[] args) {
        ThreadLocal<Integer>  threadLocal = new ThreadLocal<Integer>(){
            protected Integer initialValue() {
                return 0;
            }
        };
        for(int i = 0 ; i < 5 ; i++){
            new Thread(()->{
                int i1 = threadLocal.get().intValue()+5;
                System.out.println(Thread.currentThread().getName()+"->"+i1);
            },"Thread"+i).start();
        }
    }
}

结果,没个线程都有将自己独立的变量的副本。

Thread0->5
Thread4->5
Thread3->5
Thread2->5
Thread1->5

另外还要一个列子需要注意

public class ThreadLocaTest {
    private static Index index = new Index();
    static class Index{
        int num;
        public void incr(){
            num++;
        }
    }
    public static void main(String[] args) {
        ThreadLocal<Index>  threadLocal = new ThreadLocal<Index>(){
            protected Index initialValue() {
                return index;
            }
        };
        for(int i = 0 ; i < 5 ; i++){
            new Thread(()->{
                Index index = threadLocal.get();
                index.incr();
                System.out.println(Thread.currentThread().getName()+"->"+threadLocal.get().num);
            },"Thread"+i).start();
        }
    }
}

输出结果为:

Thread0->1
Thread2->3
Thread1->2
Thread4->5
Thread3->4

我们发现ThreadLocal并没有像预期的那样:每个线程拥有一个单独的局部变量。为什么呢?之前传递Integer是值传递,但是这次传递的是Index的引用。引用指向的是同一个地址。所以改变一个会对原来的对象也进行改变。如果要实现每个线程都有自己的局部变量,那么这么写。直接new 就好了

public class ThreadLocaTest {
    private static Index index = new Index();
    static class Index{
        int num;
        public void incr(){
            num++;
        }
    }
    public static void main(String[] args) {
        ThreadLocal<Index>  threadLocal = new ThreadLocal<Index>(){
            protected Index initialValue() {
                return new Index();
            }
        };
        for(int i = 0 ; i < 5 ; i++){
            new Thread(()->{
                Index index = threadLocal.get();
                index.incr();
                System.out.println(Thread.currentThread().getName()+"->"+threadLocal.get().num);
            },"Thread"+i).start();
        }
    }
}

结果是:

Thread0->1
Thread3->1
Thread2->1
Thread1->1
Thread4->1

3. ThreadLocal的原理

首先看ThreadLocal.get() 方法

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

getMap(t)

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

可以看到,返回的是Thread类中的一个ThreadLocalMap,就是说,每个线程都有一个ThreadLocalMap
在这里插入图片描述

接着往下看,如果ThreadLocalMap 不等于null就直接取值,如果为Null就SetInitialValue(),

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
private T setInitialValue() {
	// 我们当初在新建ThreadLocal的时候重写了InitialValue() 方法,所以这里调用的是我们重写的方法
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
    	// 如果map为null,那么就新建一个Map
        createMap(t, value);
    return value;
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //其实就是新建了一个Entry的数组
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值