ThreadLocal是Java中的线程局部变量,用于存放线程的局部变量。java
ThreadLocal为每一个线程的中并发访问的数据提供一个副本,经过访问副原本运行业务,这样的结果是耗费了内存,可是确避免线程同步所带来性能消耗,也减小了线程并发控制的复杂度。mysql
首先看一下ThreadLocal的API:sql
能够看出ThreadLocal内部应该就是封装了一个Map,本身实现ThreadLocal:数据库
public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { //①键为线程对象,值为本线程的变量副本 valueMap.put(Thread.currentThread(), newValue); } public Object get() { Thread currentThread = Thread.currentThread(); //②返回本线程对应的变量 Object o = valueMap.get(currentThread); //③若是在Map中不存在,放到Map中保存起来 if (o == null && !valueMap.containsKey(currentThread)) { o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; } }
以上代码很好理解,JDK中的实现比这复杂,能够自行查看源码。安全
ThreadLocal对象一般用于防止对可变的单实例变量或全局变量进行共享。服务器
当一个类中使用了static成员变量的时候,必定要多问问本身,这个static成员变量须要考虑线程安全吗?也就是说,多个线程须要独享本身的static成员变量吗?若是须要考虑,不妨使用ThreadLocal。多线程
例如,在单线程应用程序中可能会维护一个全局的数据库链接,并在程序启动的时候初始化这个链接,从而避免在调用每一个方法的时候都要传递一个Connection对象。因为JDBC的链接对象不必定时线程安全的,所以,当多线程应用程序在没有协同的状况下使用全局变量时,就是否是线程安全的。经过把JDBC的链接保存到ThreadLocal对象中,每一个线程都会拥有本身的链接。架构
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionManager { private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { Connection conn = null; try { conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/test", "username", "password"); } catch (SQLException e) { e.printStackTrace(); } return conn; } }; public static Connection getConnection() { return connectionHolder.get(); } public static void setConnection(Connection conn) { connectionHolder.set(conn); } }
只要使用了“池”(线程池、链接池),在使用ThreadLocal时,尤为须要注意,每一个线程在使用ThreadLocal的时候,必须对ThreadLocal执行一次clear操做,避免出现线程污染问题。并发
线程池中的线程是重复利用的,只要线程还在,ThreadLocal线程本地变量会一直存在系统中,在JavaEE的服务器中尤其明显。ide
引用高广超在Java解读-ThreadLocal详解与应用中所说:
根据池中的线程数量(在运行环境中大于100个线程是正常的)以及ThreadLocal变量中对象的大小,可能会发生致命的内存问题。例如对线程池中的200个线程进行配置以及将ThreadLocal变量的大小设置为5MB,这将会致使有1GB的堆空间被这些变量所占用。这将会致使一个GC的开销而且可能会因为OutOfMemoryError致使JVM崩溃。
参考资料: