并发和多线程(十二)--ThreadLocal
ThreadLocal是什么?
当使用ThreadLocal修饰变量的时候,ThreadLocal会为每个使用该变量的线程提供独立的变量副本,每个线程可以独立改变自己的副本,而不
影响其他线程的变量副本。
相对于synchronized和lock实现对共享资源的操作互斥而实现原子性,这是一种新的思路解决并发问题。
原理:
public class Thread implements Runnable { ThreadLocal.ThreadLocalMap threadLocals = null; }
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0; private int threshold; // Default to 0 private void setThreshold(int len) { threshold = len * 2 / 3; } private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } }
ThreadLocalMap:
ThreadLocal的内部类,类似Hashmap结构,以ThreadLocal为key,需要隔离的数据为value的Entry键值对数组结构。
Entry继承了WeakReferences,只要发生GC,key为null的entry就会被清理掉
get()源码:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap if (map != null) {//如果不为空 ThreadLocalMap.Entry e = map.getEntry(this);//取出对应位置的Entry if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value;//取出当前ThreadLocal对应的value值,返回 return result; } } return setInitialValue();//如果没取到,进行初始化 }
getMap()源码:
ThreadLocalMap getMap(Thread t) { return t.threadLocals;//获取线程的ThreadLocals,也就是ThreadLocal.ThreadLocalMap }
setInitialValue()源码:
private T setInitialValue() { T value = initialValue();//自定义初始化 Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)//有对应的map,直接set map.set(this, value); else //否则创建新的map,保存当前线程内部 createMap(t, value); return value; }
set()源码:和前面一样
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
remove()源码:
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
private void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }
应用:
数据库连接、Session管理、用户管理
public class UserContext { private static ThreadLocal<User> userHolder = new ThreadLocal<User>(); public static void setUser(User user) { userHolder.set(user); } public static User getUser() { return userHolder.get(); } }
下面两段代码来自:https://www.cnblogs.com/dolphin0520/p/3920407.html
public static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> { Connection conn = null; try { conn = DriverManager.getConnection("", "", ""); } catch (SQLException e) { e.printStackTrace(); } return conn; }); public static Connection getConnection() { return connectionHolder.get(); }
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; }
与Thread同步机制的比较:
ThreadLocal:用于线程间的数据隔离,适用于多实例对象的访问,并且这个对象很多地方都要用到
Synchronized:用于线程间的数据共享
Spring中的应用:
只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(
如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让
它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。