ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量
可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //得到线程的ThredLocalMap ThreadLocalMap map = getMap(t); //如果map不为空,则将当前线程的对象作为key,传进来的参数作为value存储 if (map != null) map.set(this, value); else createMap(t, value); }
看一下ThredLocalMap是什么:
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的一个内部类,使用Entry类进行存储。K是我们的ThredLocal对象。
总结:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象
再来看下get方法:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
拿到这个entry的value。
ThreadLocal本身并不存值,它只是作为ThreadLocalMap的key,来获取value,因此能实现数据隔离。
注意:由于ThreadLocalMap的生命周期和Thread一样长,因此要手动remove掉对应的key,不然会造成内存泄露。
使用场景:
1.管理Connection,尤其是管理数据库连接。
频繁创建和关闭connection是一件很耗时的操作,因此要用到数据库连接池。ThreadLocal可以很好的管理数据库连接,因为它能够实现当前线程的操作都是用同一个Connection,保证了事务!
public class ConnectionUtil { private static Logger logger = LoggerFactory.getLogger(ConnectionUtil.class); //数据库连接池 private static BasicDataSource dataSource; //为不同的线程管理连接 private static ThreadLocal<Connection> local; static { BufferedReader br = null; Properties ipp_prop = new Properties(); try { String propertiesurl = System.getProperty("user.dir") + "/ipp_parser.properties"; br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(propertiesurl)), "utf-8")); ipp_prop.load(br); br.close(); } catch (Exception e1) { e1.printStackTrace(); } dataSource = new BasicDataSource(); dataSource.setDriverClassName(ipp_prop.getProperty("db.driver")); dataSource.setUrl(ipp_prop.getProperty("db.url")); dataSource.setUsername(ipp_prop.getProperty("db.user")); dataSource.setPassword(ipp_prop.getProperty("db.password")); //初始连接 dataSource.setInitialSize(Integer.parseInt(ipp_prop.getProperty("db.initsize"))); //最大连接 dataSource.setMaxTotal(Integer.parseInt(ipp_prop.getProperty("db.maxtotal"))); //最长等待时间 dataSource.setMaxWaitMillis(Integer.parseInt(ipp_prop.getProperty("db.maxwait"))); //最小空闲 dataSource.setMinIdle(Integer.parseInt(ipp_prop.getProperty("db.minidle"))); dataSource.setMaxIdle(Integer.parseInt(ipp_prop.getProperty("db.maxidle"))); //初始化线程池本地 local = new ThreadLocal<>();/**得到连接 * @return * @throws SQLException */ public static Connection getOracleConnection() throws SQLException { //获取Connection对象 Connection connection = dataSource.getConnection(); //把Connection放进local里 local.set(connection); logger.info("get oracleConnection"); return connection; } public static void closeOracleConnection(){ Connection connection = local.get(); try { if (connection != null) { //设置自动提交 connection.setAutoCommit(true); //连接还给连接池 connection.close(); local.remove(); logger.info("close oracleConnection"); } } catch (SQLException e) { e.printStackTrace(); } } }