1.ThreadLocal源码解读
(1)get()
get()方法是用来获取ThreadLocal在当期线程中保存的变量副本。
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();
}
再看一下getMap方法中的操作
ThreadLocalMap getMap(Thread t)
{
return t.threadLocals;
}
可以看出,实际上是从当前线程t中获得成员变量threadLocals。那么再看一下Thread类中的threadLocals成员变量
ThreadLocal.ThreadLocalMap threadLocals = null;
发现其实也就是一个ThreadLocal类中定义的一个内部类ThreadLocalMap类的对象,继续看ThreadLocalMap的实现:
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;
}
}
.......
}
那么就可以看出,实际上ThreadLocalMap也是一个Map,将ThreadLocal作为key
继续看get()中的setInitialValue()方法
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不为空,则直接将this作为key存储到ThreadLocalMap中,否则则创建Map,creteMap方法为:
void createMap(Thread t, T firstValue)
{
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
也就是说如果map为空,则直接创建一个即可
(2)set(T value)
set(T value)方法是用来设置当期线程中变量的副本。
public void set(T value)
{
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看出,也就是在当前线程的Thread对象中的ThreadLocalMap中设置对应的值
2.ThreadLocal应用
看一个例子
public class ThreadLocalTest
{
public static void main(String[] args) throws InterruptedException
{
final Local local = new Local();
local.set(1,"main");
new Thread(new Runnable()
{
@Override
public void run()
{
local.set(2,"sub");
local.show();
}
}).start();
Thread.sleep(1000);
local.show();
}
}
class Local
{
private ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
private ThreadLocal<String> strLocal = new ThreadLocal<String>();
public void set(long l,String n)
{
longLocal.set(l);
strLocal.set(n);
}
public void show()
{
System.out.println(longLocal.get());
System.out.println(strLocal.get());
}
}
结果为:
2
sub
1
main
可以看出,每个线程都有自己的值,说明ThreadLocal起作用了
3.ThreadLocal理解
由于是在Thread类中定义一个Map,所以必然就能不同线程不同值
4.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();
}
}
在单线程程序中,这段代码没有任何问题,但是放在多线程情况下,会存在以下两个问题:1.两个方法都没有进行同步,可能会在openConnection中多次创建connect 2.由于connect是共享变量,所以所有线程都是用这一个变量,那么一个线程在是用的时候,其他线程很有可能在closeConnextion关闭连接,从而导致问题 所以处于安全考虑,必须将这两个方法进行同步处理。
但是由于所有线程共享一个connect,所以当一个线程在进行同步操作的时候,其他线程只能等待,这样会使效率非常低
实际上,数据库连接完全可以不用共享,那么直接改为在方法中调用如何呢?
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会对每一个线程内部构造一个副本,并且在线程内部任何地方都可以使用,这样一来就不存在线程安全问题,也不会严重影响程序性能。
ThreadLocal最适合这种每个线程都需要保存自己副本的情况,比如数据库连接池,Session等
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>()
{
public Connection initialValue()
{
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection()
{
return connectionHolder.get();
}
重写了initialValue方法,也就是在线程初始化的时候创建一个数据库连接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;
}
用来管理Session