说明:本文是JDK 1.8版本
1.简介:
ThreadLocal 又叫做线程本地变量,也被称为线程本地存储。ThreadLocal 为 变量 在每一个线程中创建 一个 副本(不是原来变量的引用),每一个线程都会独自拥有变量副本,而不会相互影响。
2.实现方式:
(1)set 方法,,因为线程Thread 类中 内置了 ThreadLocal.ThreadLocalMap 类型的引用threadLocals,所以 set方法把把 用户变量 存储到线程的 变量threadLocals中
public void set(T value) { Thread t = Thread.currentThread();//得到当前线程 ThreadLocalMap map = getMap(t);//得到线程中 threadLocals的值 if (map != null) map.set(this, value);//如果当前不为空则设置 键值对,键为 当前的ThreadLocal else createMap(t, value);//如果为空则创建一个ThreadLocalMap }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); //把用户变量 存放到 线程中 }ThreadLocalMap 里面 使用了Entry 存储数据,键 为 ThreadLocal,值为用户变量
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
(2) get方法,获取当前线程中存储的用户变量
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//得到当前线程中存储的用户变量 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this);//得到指定 ThreadLocalMap的 存储的用户变量值 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue();//设置初始化的值 }
private T setInitialValue() { T value = initialValue();//默认返回为null ,用户可以重写 改方法,实现自己想要的结果 Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //得到当前 线程的 本地变量值 if (map != null) map.set(this, value); // 如果线程中存在 ThreadLocalMap存在 则 存储 用户变量到线程 else createMap(t, value); //如果不存在 则新增一个到当前线程中 return value; }
protected T initialValue() { return null; }
3.代码验证 用户变量是否存放到 线程中:
方式一:不重写initialValue方法
public class ThreadLocalDemo1 { ThreadLocal<String> stringThreadLocal = new ThreadLocal<String>(); ThreadLocal<Long> longThreadLocal = new ThreadLocal<Long>(); public void set(){ stringThreadLocal.set(Thread.currentThread().getName()); longThreadLocal.set(Thread.currentThread().getId()); } public void show(){ System.out.println(longThreadLocal.get()); System.out.println(stringThreadLocal.get()); } public static void main(String[] args) throws Exception{ ThreadLocalDemo1 demo1 = new ThreadLocalDemo1(); demo1.set(); demo1.show(); Thread t = new Thread(){ @Override public void run() { demo1.set(); demo1.show(); } }; t.start(); t.join(); demo1.show(); } }输出结果:
1
main
11
Thread-0
1
main
在线程t 没创建之前,存放在线程main 函数中,而线程 t 中 又重新赋值,输出显示的是一个新的 线程, 在线程结束 之后,直接输出 ,仍然是进入线程
之前存放的值,因此可以看到 他们是 在每个线程中 创建了副本
方式二:重写initialValue方法
public class ThreadLocalDemo2 { ThreadLocal<String> stringThreadLocal = new ThreadLocal<String>(){ @Override protected String initialValue() { return Thread.currentThread().getName(); } }; ThreadLocal<Long> longThreadLocal = new ThreadLocal<Long>(){ @Override protected Long initialValue() { return Thread.currentThread().getId(); } }; public void show(){ System.out.println(longThreadLocal.get()); System.out.println(stringThreadLocal.get()); } public static void main(String[] args) throws Exception{ ThreadLocalDemo2 demo1 = new ThreadLocalDemo2(); demo1.show(); Thread t = new Thread(){ @Override public void run() { demo1.show(); } }; t.start(); t.join(); demo1.show(); } }
输出结果:
1
main
11
Thread-0
1
main
ThreadLocal 经常在管理数据库连接,session 管理 中使用 :
使用场景的代码摘抄自(摘抄自:http://www.importnew.com/17849.html)
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } };
private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
参考博客:
http://www.importnew.com/17849.html