由于Alibaba面试官提到ThreadLoacl,但是由于之前根本没有使用过,也没真正的去专研这个类。连夜赶出一篇文章。
ThreadLocal
ThreadLocal是解决多线程访问同一变量,保证线程安全产生的。它为每一个使用变量的线程提供一个独立的副本。换句话说,每个线程都持有一个该对象的副本。自己对自己的副本进行操作就不会出现线程不安全的问题。它将每个线程和对象的副本存放在ThreadLocalMap(ThreadLocal的一个静态内部类)中,ThreadLocalMap下面是一个Entry[]数组。
在创建ThreadLocal时,ThreadLocalMap内是空的,当调用get()/set()方法时,就会对其中的ThreadLocalMap获取/添加操作。
package com.example.demo.juc;
/**
* ThreadLocalTest
* @author Logan
*/
public class ThreadLocalTest {
// long Thread Local
private ThreadLocal longLocal = new ThreadLocal();
// string Thread Local
private ThreadLocal stringLocal = new ThreadLocal();
public static void main(String[] args) throws InterruptedException {
final ThreadLocalTest threadLocalTest = new ThreadLocalTest();
// 设置值
threadLocalTest.set();
System.out.println("===1===");
System.out.println(threadLocalTest.getLongLocal().get());
System.out.println(threadLocalTest.getStringLocal().get());
// 创建线程
Thread thread = new Thread(()->{
// 当这里调用了set方法,进一步调用了ThreadLocal的set方法是,
// 会将ThreadLocal变量存储到该线程的ThreadLocalMap类型的成员变量threadLocals中,
// 注意的是这个threadLocals变量是Thread线程的一个变量,而不是ThreadLocal类的变量。
threadLocalTest.set();
System.out.println("===2===");
System.out.println(threadLocalTest.getLongLocal().get());
System.out.println(threadLocalTest.getStringLocal().get());
});
thread.start();
thread.join();// 强行加入
System.out.println("===3===");
System.out.println(threadLocalTest.getLongLocal().get());
System.out.println(threadLocalTest.getStringLocal().get());
}
/**
* 设置线程的Id 和 Name
*/
private void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public ThreadLocal getLongLocal() {
return longLocal;
}
public ThreadLocal getStringLocal() {
return stringLocal;
}
}
ThreadLocal 的应用场景
1.获取数据库连接。
class DBTest {
private final static String DB_URL = "url";
private static ThreadLocal connectionThreadLocal = new ThreadLocal(){
@Override
protected Connection initialValue() {
try {
Connection connection = DriverManager.getConnection(DB_URL);
connectionThreadLocal.set(connection);
return connection;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
};
private static Connection getConnect(){
return connectionThreadLocal.get();
}
}
2.Session的管理
class SessionTest{
private static ThreadLocal sessionThreadLocal = new ThreadLocal();
public static Session getSession() {
Session session = (Session) sessionThreadLocal.get();
if(session == null){
session = getSessionFactory().openSession();
}
return session;
}
}
All in All : ThreadLocal是为了保证线程安全的,但是需要复制大量的副本,这也不是一个好事,增大的内存的开销。
线程安全还可以使用ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArrayList等容器,也可以保证线程安全。