源码说ThreadLocal

学习Java中常用的开源框架,MybatisHibernate中设计到线程通过数据库连接对象Connection,对其数据进行操作,都会使用ThreadLocal类来保证Java多线程程序访问和数据库数据的一致性问题。就想深入了解一下ThreadLocal类是怎样确保线程安全的!详解如下:


说起ThreadLocal之前我想带大家先去看看Thread中的两个变量方便后面的理解。

Thread类

public
class Thread implements Runnable {
 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    }

Thread源码得知每个Thread类都会有着两个变量threadLocalsinheritableThreadLocals并且都是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap,在默认情况下,每个线程中的这两个变量都为null。知道了这些我们再看看今天的主菜ThreadLocad

ThreadLocad

  • set方法
   public void set(T value) {
         //1.获取当前线程
       Thread t = Thread.currentThread();
       //2.将当前线程作为key,去查找对应的线程变量,找到则设置
       ThreadLocalMap map = getMap(t);
       if (map != null)
           map.set(this, value);
       else
         //3.第一次调用就创建当前线程对应的HashMap
           createMap(t, value);
   }

代码1首先获取调用线程,然后使用当前线程作为参数调用getMap(t)方法,
getMap(Thread t)的代码如下:

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

可以看到,getMap(t)的作用是获取线程自己的变量threadLocalsthreadLocals变量被绑定到了线程的成员变量上。
如果getMap(t)的返回值不为空,则把value值设置到threadLocals中,也就是把当前变量值放入当前线程的内存变量threadLocals中。threadLocals是一个HahsMap结构,其中key就是当前ThreadLocad的实例对象引用,value是通过set方法传递的值。
如果getMap(t)的返回值为空,说明是第一次调用set方法,这时创建当前线程的threadLocals变量。下面看看createMap(t,value)做什么。

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

他创建当前线程的threadLocals变量。

-get方法

   public T get() {
   //1.获取当前线程
        Thread t = Thread.currentThread();
   //2.获取当前线程的threadLocals变量     
        ThreadLocalMap map = getMap(t);
   //3.如果threadLocals不为null,则返回对应的本地变量的值     
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
    //4.threadLocals为空则初始化当前线程的threadLocals成员变量    
        return setInitialValue();
    }

代码1首先获取当前线程实例,如果当前线程的threadLocals变量不为null,则直接返回当前线程绑定的本地变量,否则执行代码4进行初始化。setInitialValue()代码如下:

 private T setInitialValue() {
 //初始化为null
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
   //如果当前线程的threadLocals变量不为空     
        if (map != null)
            map.set(this, value);
        else
   //如果当前线程的threadLocals变量为空      
            createMap(t, value);
        return value;
    }

如果当前线程的threadLocals变量不为空,则设置当前线程的本地变量为null,否则调用createMap方法创建当前线程的threadLocals变量。


总结:

我个人认为ThreadLocad就是一个工具类,他通过set方法把value值放入调用线程的threadLocals里面并存放起来,当调用get方法时,再从当前线程的threadLocals变量里面将其拿出来使用。

小案例

两个线程去操作一个变量

public class ThreadLocadDemo1 {

    static Integer i = 10;
    static ThreadLocal<Integer> threadLocal=new ThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
           threadLocal.set(i);
           add();
        });

        Thread t2=new Thread(()->{
            threadLocal.set(i);
            del();
        });

        t1.start();
        t2.start();

        Thread.sleep(2000);
        System.out.println(i);
    }

    private static void del() {
        Integer integer = threadLocal.get();
        System.out.println(integer-5);
    }

    private static void add() {
        Integer integer = threadLocal.get();
        System.out.println(integer+20);
    }

}

执行结果
5
30
10
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值