- 线程本地—->线程同步的一种方式
- 做到某个对象每个线程中独有一份,使一个线程中修改的内容不会影响到另外一个线程
- 用ThreadLocal的时候,里面设的值是线程独有的
- 每一个线程往里面设置值,设置的都是自己能够访问到的变量
- 这是怎样做到的?—->ThreadLocal的源码
/**
* ThreadLocal线程局部变量
*
* ThreadLocal是使用空间换时间,synchronized是使用时间换空间
* 比如在hibernate中session就存在与ThreadLocal中,避免synchronized的使用
*
* 运行下面的程序,理解ThreadLocal
*
* @author 马士兵
*/
package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
import java.util.concurrent.TimeUnit;
public class ThreadLocal2 {
//volatile static Person p = new Person();
static ThreadLocal<Person> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tl.get());
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tl.set(new Person());
}).start();
}
static class Person {
String name = "zhangsan";
}
}
ThreadLocal源码
- set方法(往里面设值)
- 先拿到当前线程
- 获取当前线程对应的ThreadLocalMap容器—->是一个键值对
- 这个容器的key是this当前对象,value是我们想要的那个值
- 这里的this就是那个ThreadLocal对象
- 如果不存在这个容器(返回值为null),就用当前线程和要设置的值创建这样的map容器
- 如果存在这个容器,就直接将当前要保存的值设置进去
- ThreadLocalMap在Thread类中,是Thread的一个成员属性threadLocals
- ThreadLocal进行set是把他set到当前线程中的某一个map里去了
- ThreadLocalMap是ThreadLocal的一个静态内部类,而ThreadLocalMap中存的其实是Entry对象的数组(这个数组实际上就是Hash桶,用来进行hash散列的,实际上他自己实现了一个哈希表)(Entry类是ThreadLocalMap的静态内部类,Entry本身是个ThreadLocal类型的软引用,其中还有一个Object类型的value—->由此构成键值对)
- 为什么一个线程中使用了set,但另一个线程中依然获取不到?
- 因为当前线程和另一个线程中各有一个map,这是不同的map
- 自己线程特有的,其他线程没有的
- 理解成所有线程的变量都放在一个map是错误的
- 一个线程可以对应多个ThreadLocal,将这些ThreadLocal与其对应值分别作为键和值存进Thread中的ThreadLocalMap中去
- 与以往的认知不一样,不是所有的线程共享这个ThreadLocalMap,然后根据线程为key去取各自的变量;而是每个线程都是有自己的ThreadLocalMap,里面可以放key为ThreadLocal,value为具体对象的键值对,并且可以放多个,是属于这个线程自己享有的,其他线程没有—->线程的概念:继承了Thread类的线程,然后每new出来一个对象都属于不同的线程,即由同一个线程类产生出不同的线程对象!!!
- 为什么要用ThreadLocal?
- Spring的声明式事务@Transactional(Spring或者Hibernate)
- 不理解声明式事务就不知道ThreadLocal在哪用
- 声明式事务一般是要通过数据库的,但Spring结合mybatis的写法—->可以把整个事务写在配置文件中,而配置文件中的事务其实是管理了一系列的方法(1、2、3、4、……),方法中写了去配置文件拿到数据库连接之类的一系列操作(比如有好几个拿数据库连接的操作),声明式事务可以把这一系列的操作合到一起视为一个完整的事务
- 如果上述c中多次拿到数据库连接时,拿到的不是同一个对象,不能形成一个完整的事务
- connection是会放到连接池里的,如果多次连接拿到的是不同的connection,这样就不能形成一个完整的事务—->不同的connection之间是不能形成一个完整的事务的,这是不行的,不可能的—->这就要保证拿到的是同一个connection
- 把某一个connection放到线程的本地对象ThreadLocal里,以后拿的时候是从ThreadLocal里面直接拿,第一个方法拿的时候,把获取到的connection放到ThreadLocal中,后面的方法要拿的时候从ThreadLocal中直接拿,不从线程池中拿
- 用途:
- 声明式事务保证同一个Connection
- 切换数据源(用ThreadLocal,当前线程的记录下来,其他人拿到要用的时候从当前线程中直接取)
- 理解含义之后灵活运用即可