一、什么是ThreadLocal?
多线程之间的通信可以通过共享变量来实现(通常以 public static 来修饰共享变量),当有多个线程对共享变量进行操作时,为保证其安全性,我们通常需要对其进行同步处理来保证安全性。但是这又会造成程序执行效率的降低。
在某些情况下,若我们是对共享变量的副本进行操作,而非直接操作其本体,那么就可以在既保证效率的情况下又保证其安全性(如数据库连接池获取connection,以及getsession等场景),这个时候我们的主角ThreadLocal就出现了。
ThreadLocal,线程本地变量。ThreadLocal为共享变量在每个线程中都创建了一个副本,每个线程都可以访问自己内部的副本变量。各个线程之间的变量互不干扰。
二、ThreadLocal的基本操作
方法名 | 作用 |
---|---|
public T get() { } | 获取当前线程中共享变量的变量副本 |
public void set(T value) { } | 设置当前线程中共享变量的变量副本 |
public void remove() { } | 移除当前线程中共享变量的变量副本 |
protected T initialValue() { } | 为当前线程中共享变量的变量副本进行初始化 |
实例测试:
public class Test {
ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
ThreadLocal<String> stringLocal = new ThreadLocal<String>();
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public long getLong() {
return longLocal.get();
}
public String getString() {
return stringLocal.get();
}
public static void main(String[] args) throws InterruptedException {
final Test test = new Test();
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
Thread thread1 = new Thread(){
public void run() {
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
};
};
thread1.start();
thread1.join(); // 这里join方法的作用就是: 主线程需要等待子线程执行完成之后再执行
System.out.println(test.getLong());
System.out.println(test.getString());
}
}
/*
get前若想不set且能正常访问的话,则必须重写initeValue()方法,重写部分如下:
ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
protected Long initialValue() {
return Thread.currentThread().getId();
};
};
ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
protected String initialValue() {
return Thread.currentThread().getName();
};
};
*/
测试结果:
结论:
1.在多线程执行过程中,main线程和thread1线程执行时分别保存了共享变量的副本值,且两线程互不干扰。
(第二次打印thread1的值是为了证明二者保存的副本值确实是不同的)
2.每个线程中可以有多个threadlocal变量
3.在进行get之前,必须进行set否则会报空指针异常。若想不set直接get且能正常访问的话,则必须重写initeValue()方法。
三、ThreadLocal实际应用场景
3.1 数据库连接场景
不使用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();
}
}
使用threadlocal解决:
private static ThreadLocal<Connection> connectionHolder= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
3.2 Session管理
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;
}
参考来源:
本文没有对threadlocal底层源码进行深入解析,只是在理解与应用上做了简单阐述。详细内容参考大神博文。