ThreadLocal:是线程的本地变量,也叫本地存储。ThreadLocal为变量在每个线程中创建了一个副本,那么每个线程可以访问自己内容的副本变量
ThreadLocal应用场景:
比如
class ConnectionManager { private static Connection connect = null; public static Connection openConnection() { if(connect == null){ connect = DriverManager.getConnection(); } return connect; } public static void closeConnection() { if(connect!=null) connect.close(); } }
这是一个数据库连接管理类,当多个线程同时调用的时候就会出现线程安全问题。
避免线程安全问题:
可以在方法前加synchronized修饰,但是这样就会大大影响了效率
这里的这个例子可以不用connection共享,那么可能有的人就会想到,既然不用共享,那么我直接每个线程new一个出来。虽然这样是不会造成线程安全问题,而且线程与线程之间不必相互等待了,但是这样可能会导致服务器压力太大,因为每个线程都有一个connection,会严重影响性能
class ConnectionManager { private Connection connect = null; public Connection openConnection() { if(connect == null){ connect = DriverManager.getConnection(); } return connect; } public void closeConnection() { if(connect!=null) connect.close(); } } class Dao{ public void insert() { ConnectionManager connectionManager = new ConnectionManager(); Connection connection = connectionManager.openConnection(); //使用connection进行操作 connectionManager.closeConnection(); } }
那么出现这种情况我们可以用ThreadLocal类
ThreadLocal 提供了四个方法
public T get() { } public void set(T value) { } public void remove() { } protected T initialValue() { }
get()获取ThreadLocal在当前线程中保存的变量副本
set()用来设置当前线程中变量副本
remove()用来移除当前线程变量副本
initialValue()是一个protected方法,一般用来重写,是一个延迟加载方法
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ 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(); }
get方法会掉用getMap()方法,如果map不为空,然后会取到<key,value>键值对
/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
注意这里取键值对传的是this,而不是当前线程t。
如果map为空则调用setInit方法返回value
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
getMap方法返回的是threadLocals,而threadlocals实际上就是一个ThreadLocalMap对象
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap内部实现
static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
实际上是取的ThreadLocal作为键值
set方法
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ 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
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
* Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
每个线程内部有一个ThreadLocalMap的成员变量threadLocals,这个变量实际上是来存储实际变量副本的,键值为当前ThreadLocal、变量,value为变量副本
初始时,threadLocals为空,会通过get(),set()方法对threadLocals初始化,并且以当前线程变量为键值进行赋值
在当前线程里面,如果要使用本地变量,就可以通过get方法在threadLocals里面查找
public class Test { ThreadLocal<Long> longLocal = new ThreadLocal<Long>(); ThreadLocal<String> stringLocal = new ThreadLocal<String>(); public void set() { longLocal.set(Thread.currentThread().getId()); stringLocal.set(Thread.currentThread().getName()); } public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test test = new Test(); test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(){ public void run() { test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); }; }; thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); } }
输出结果
1
main
2
thread0
1
main
所以可以看出,threadLocal存的是线程的本地变量,main线程,和thread1线程存取的值都不同