初识ThreadLocal

今天来学习Java中的ThreadLocal,也叫做本地变量,主要有如下知识点:数据库

  • 基本使用
  • 源码解析
  • 使用场景

概述

ThreadLocal,不少地方叫作线程本地变量。
ThreadLocal在每一个线程中为变量建立一个副本,即每一个线程内部都会有一个该变量,且在线程内部任何地方均可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。安全

源码解析

ThreadLocal类提供的几个方法:多线程

public T get() {}
public void set(T value) {}
public void remove() {}
protected T initialValue() {}

get()方法是用来获取ThreadLocal在当前线程中保存的变量副本.
set()用来设置当前线程中变量的副本.
remove()用来移除当前线程中变量的副本.
initialValue()是一个protected方法,通常是用来在使用时进行重写.性能

首先咱们来看一下ThreadLocal类是如何为每一个线程建立一个变量的副本的。学习

1.先看下get方法的实现:this

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

第一句是取得当前线程,而后经过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap
而后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是this,而不是当前线程t。
若是获取成功,则返回value值。
若是map为空,则调用setInitialValue方法返回value。线程

2.接着看一下getMap方法中作了什么:code

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

getMap中,是调用当前线程t,返回当前线程t中的一个成员变量threadLocals对象

3.那么咱们继续取Thread类中取看一下成员变量threadLocals是什么:继承

ThreadLocal.ThreadLocalMap threadLocals = null;

实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,咱们继续取看ThreadLocalMap的实现:

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal> {
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
            
        }
    }
}

能够看到ThreadLocalMap的Entry继承了WeakReference,而且使用ThreadLocal做为键值

4.而后再继续看setInitialValue方法的具体实现:

private T setInitialValue() {
    
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
    
}

很容易了解,就是若是map不为空,就设置键值对,为空,再建立Map,看一下createMap的实现:

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

至此,可能大部分朋友已经明白了ThreadLocal是如何为每一个线程建立变量的副本的:

首先,在 每一个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,key值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当经过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,而且 以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。而后在当前线程里面,若是要使用副本变量,就能够经过get方法在threadLocals里面查找。

总结

  • 每一个Thread维护着一个ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  • 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  • 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
  • ThreadLocal自己并不存储值,它只是做为一个key来让线程从ThreadLocalMap获取value

使用场景

最多见的ThreadLocal使用场景为用来解决数据库链接、Session管理等。

数据库链接

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>(){
    
    public Connection initialvalue () {
      return DriverManager.getConnection(DB_URL);
}
    public static Connection getConnection () {
      return connectionHolder.get();
   }
};

有关多线程的知识暂时先到这里,后面有时间再补充!

相关文章
相关标签/搜索