ThreadLocal全面解析(附源码)

1.为什么要使用ThreadLocal

多线程访问同一个变量时,特别容易出现并发问题,特别是在写入时。为了保证线程的安全,一般在访问共享变量时需要进行同步。同步一般是通过加锁来实现,但这对使用者的要求比较高,需要对锁有一定的了解,对小白非常的不友好。
在这里插入图片描述

       那有没有一种方式可以做到当一个变量创建后,每个线程对其进行访问时都访问的是自己的变量呢?

       ThreadLocal就可以解决这个问题。Threadlocal是JDK包提供的,它提供了线程本地变量。也就是当你创建了一个ThreadLocal本地变量时,访问这个变量的每个线程都会有这个变量的一个本地副本。当多线程操作这个变量时,就相当于在操作自己本地内存里的变量,从而避免了线程安全问题。创建了一个ThreadLocal变量后,每个线程都会将变量复制到自己的本地内存中,如下图:
在这里插入图片描述

2.ThreadLocal如何使用?

ThreadLocal的使用很简单,直接上代码:

publicpublic class ThreadLocalTest {
    static void print(String str) {
        //打印本地内存中的loacalVariable变量
        System.out.println(str + ":" + localVariable.get());
        //清除本地内存中的loacalVariable变量
        localVariable.remove();
    }
    //创建loacalVariable变量
    static ThreadLocal<String> localVariable = new ThreadLocal<>();

    public static void main(String args[]){

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //设置线程1中loacalVariable的值
                localVariable.set("thread1 LocalVariable");
                //打印线程1中loacalVariable的值
                print("thread1");
                System.out.println("thread1 remove after:"+localVariable.get());
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //设置线程2中loacalVariable的值
                localVariable.set("thread2 LocalVariable");
                //打印线程2中loacalVariable的值
                print("thread2");
                System.out.println("thread2 remove after:"+localVariable.get());
            }
        });

        thread1.start();
        thread2.start();
    }
}

上述代码分别创建了两个线程。在线程中通过set方法设置本地localVariable的值,每个线程中都拥有localVariable的本地值,两个线程间不受影响。最后结果打印:
在这里插入图片描述

3.ThreadLocal源码解析

先看一下ThreadLocal相关类的类图
在这里插入图片描述
       可以看出,Thread类中有两个ThreadLocalMap的变量。ThreadLocalMap是一个定制化的hashmap,在默认情况下,每个线程中这两个变量的值都为null,当线程第一次调用ThreadLocal的set方法或get方法时才会创建它们。
       其实每个线程的本地变量不是存放在ThreadLocal实例中的,而是存放在线程自身的threadLocal变量里面。也就是说ThreadLocal变量只是一个空壳,它并不是真正存放线程的本地变量的地方。我们只是通过ThreadLocal变量来set或get线程里的threadLocals中的值。这也是为什么threadLocals是ThreadLocalMap的原因,因为一个线程可以存放多个ThreadLocal。
       接下来简单分析一下ThreadLocal的源码:

1.set(T value)

void set(T value) {
	//获取当前线程
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);		//根据当前线程得到它的threadLocals变量
	if (map != null)
		map.set(this, value);		//以此ThreadLocal变量为key,放入到此线程的value中
	else
		createMap(t, value);	//第一次调用,创建新的ThreadLocalMap
}

       set方法先获取了当前执行的线程,然后得到当前线程的ThreadLocalMap对象。ThreadLocalMap变量中存放了以ThreadLocal变量为Key的值。如果map对象为空,则创建新的ThreadLocalMap。getMap(Thread t)的代码如下:

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

创建ThreadLocalMap的方法createMap(Thread t ,T firstValue)代码如下:

voidcreateMap(Thread t,T firstValue){
	t.threadLocals = new ThreadLocalMap(this,firstValue);
}

2.T get()

public T get() {
	//获取当前线程
	Thread t = Thread.currentThread();
	//获取当前线程的threadLocals变量
	ThreadLocalMap map = getMap(t);
	if (map != null) {
		//使用当前ThreadLocal变量作为key,取得这个Entry
		ThreadLocalMap.Entry e = map.getEntry(this);		
		if (e != null) {
			@SuppressWarnings("unchecked")
			T result = (T)e.value;
			return result;
		}
	}
	return setInitialValue();	//ThreadLocalMaps为空,则初始化当前线程的threadLocals成员变量
}

get方法也是先获取当前线程,从当前线程的threadLocals变量中获取以当前ThreadLocal变量为key的值。如果thredLocals为空,就先创建一个。setInitialValue()方法实现如下:

private T setInitialValue() {
    T value = initialValue();		//初始化为null
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    	if (map != null)			//再一次判断当前Map是不是空
    		map.set(this, value);
    	else
    		createMap(t, value);		//本质还是调用了createMap函数
    return value;
}

protected T initialValue() {
	return null;
}

3.void remove()

public void remove(){
	ThreadLocalMap map = getMap(Thread.currentThread());
	if(m != null)
		m.remove(this);
}

如果当前线程的threadLocals不为空,则删除指定ThreadLocal实例的本地变量。

使用ThreadLocal能不能在子线程中获取父线程的值呢?答案是不行,因为子线程和父线程不是同一个线程,在执行Thread。currentThread()时,得到的不是同一个线程,所以获取不到,那有没有解决方法,让子线程能够访问到父线程的值呢?可以看我的下一篇博文InheritableThreadLocal全面解析(附源码)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值