学习ThreadLocal之前,看了很多其它的博客。但是各种博客,将的都有那么点点困惑。现在来好好梳理下。
一.ThreadLocal原理分析
ThreadLocal是线程本地变量的意思。在创建的每一个线程中,内部都有一个ThreadLocalMap 类型的成员变量对象(threadLocals),但是这个ThreadLocalMap类没有实现Map接口,而是,ThreadLocal 类的静态内部类。threadLocals这个成员变量,以键值对的形式存K-V,K为threadLocal,V为存到线程的变量。
ThreadLocal类的方法如下:
public void set(T value) //将变量拷贝到线程当中,实际是引用拷贝
public T get() //获取线程中ThreadLocals里当前的变量,以当前K--threadlocal 获取V
public void remove() //移除线程中的变量,以当前K--threadlocal 移除entry键值对
protected T initialValue()//子类重写时初始化的返回的初始对象,没有set()之前,默认为null
a.在多线程中,对同一个对象的访问,必然会出现冲突,所以要加锁来解决冲突。
b. 但是,如果多线程对这种具有同一用途的对象,比如数据库连接中的Connection对象,每个线程只需要一个Connection对象就好,并不要求相关,各处理各的,而只是单纯的使用这种对象,那就直接给每个线程这样的对象就好了。这样就实现了数据隔离。
也就是通常所说的,a情况“以时间换取效率”,b情况“以空间换取效率”。
二.ThreadLocal源码分析
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
比如,Connection connection = DriverManager.getConnection();
要把connection对象存到线程中,之后的操作与其它线程没有关系,利用ThreadLocal类的set()方法就可以做到。这个过程就是所说的创建对象的副本了,线程中有了connection的拷贝,可以看到源码中,其实是传递引用的。
import java.sql.SQLException;
/**
* @author jun
* @time 2018/10/25
*/
public class ThreadLocalTest {
public static void main(String[] args){
// 线程本地变量
final ThreadLocal tL1 = new ThreadLocal();
final ThreadLocal tL2 = new ThreadLocal();
Thread t1 = new Thread(){
@Override
public void run() {
tL1.set(C3p0Utils.getConnection());
while (true){}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
tL2.set(C3p0Utils.getConnection());
while (true){}
}
};
// 两个线程的Connection互不干扰,起到数据隔离的效果。
t1.start();
t2.start();
}
}
线程本地变量,简单来说,就是你要往你的线程里添加什么变量,添加的这个变量对其它线程来说是不可见的。而且这个变量的get()是根据当前线程本地变量名来进行匹配的。
暂且写到这里。记住是用来隔离数据的就可以了。