1. 线程本地—->线程同步的一种方式
  2. 做到某个对象每个线程中独有一份,使一个线程中修改的内容不会影响到另外一个线程
  3. 用ThreadLocal的时候,里面设的值是线程独有的
  4. 每一个线程往里面设置值,设置的都是自己能够访问到的变量
  5. 这是怎样做到的?—->ThreadLocal的源码
  1. /**
  2. * ThreadLocal线程局部变量
  3. *
  4. * ThreadLocal是使用空间换时间,synchronized是使用时间换空间
  5. * 比如在hibernate中session就存在与ThreadLocal中,避免synchronized的使用
  6. *
  7. * 运行下面的程序,理解ThreadLocal
  8. *
  9. * @author 马士兵
  10. */
  11. package com.mashibing.juc.c_022_RefTypeAndThreadLocal;
  12. import java.util.concurrent.TimeUnit;
  13. public class ThreadLocal2 {
  14. //volatile static Person p = new Person();
  15. static ThreadLocal<Person> tl = new ThreadLocal<>();
  16. public static void main(String[] args) {
  17. new Thread(()->{
  18. try {
  19. TimeUnit.SECONDS.sleep(2);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println(tl.get());
  24. }).start();
  25. new Thread(()->{
  26. try {
  27. TimeUnit.SECONDS.sleep(1);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. tl.set(new Person());
  32. }).start();
  33. }
  34. static class Person {
  35. String name = "zhangsan";
  36. }
  37. }

ThreadLocal源码

  1. set方法(往里面设值)
    1. 先拿到当前线程
    2. 获取当前线程对应的ThreadLocalMap容器—->是一个键值对
      1. 这个容器的key是this当前对象,value是我们想要的那个值
      2. 这里的this就是那个ThreadLocal对象
    3. 如果不存在这个容器(返回值为null),就用当前线程和要设置的值创建这样的map容器
    4. 如果存在这个容器,就直接将当前要保存的值设置进去

image.png

  1. ThreadLocalMap在Thread类中,是Thread的一个成员属性threadLocals
  2. ThreadLocal进行set是把他set到当前线程中的某一个map里去了
  3. ThreadLocalMap是ThreadLocal的一个静态内部类,而ThreadLocalMap中存的其实是Entry对象的数组(这个数组实际上就是Hash桶,用来进行hash散列的,实际上他自己实现了一个哈希表)(Entry类是ThreadLocalMap的静态内部类,Entry本身是个ThreadLocal类型的软引用,其中还有一个Object类型的value—->由此构成键值对
    1. 为什么一个线程中使用了set,但另一个线程中依然获取不到?
  4. 因为当前线程和另一个线程中各有一个map,这是不同的map
  5. 自己线程特有的,其他线程没有的
  6. 理解成所有线程的变量都放在一个map是错误的
  7. 一个线程可以对应多个ThreadLocal,将这些ThreadLocal与其对应值分别作为键和值存进Thread中的ThreadLocalMap中去
  8. 与以往的认知不一样,不是所有的线程共享这个ThreadLocalMap,然后根据线程为key去取各自的变量;而是每个线程都是有自己的ThreadLocalMap,里面可以放key为ThreadLocal,value为具体对象的键值对,并且可以放多个,是属于这个线程自己享有的,其他线程没有—->线程的概念:继承了Thread类的线程,然后每new出来一个对象都属于不同的线程,即由同一个线程类产生出不同的线程对象!!!
    1. 为什么要用ThreadLocal?
  9. Spring的声明式事务@Transactional(Spring或者Hibernate)
  10. 不理解声明式事务就不知道ThreadLocal在哪用
  11. 声明式事务一般是要通过数据库的,但Spring结合mybatis的写法—->可以把整个事务写在配置文件中,而配置文件中的事务其实是管理了一系列的方法(1、2、3、4、……),方法中写了去配置文件拿到数据库连接之类的一系列操作(比如有好几个拿数据库连接的操作),声明式事务可以把这一系列的操作合到一起视为一个完整的事务
  12. 如果上述c中多次拿到数据库连接时,拿到的不是同一个对象,不能形成一个完整的事务
  13. connection是会放到连接池里的,如果多次连接拿到的是不同的connection,这样就不能形成一个完整的事务—->不同的connection之间是不能形成一个完整的事务的,这是不行的,不可能的—->这就要保证拿到的是同一个connection
  14. 把某一个connection放到线程的本地对象ThreadLocal里,以后拿的时候是从ThreadLocal里面直接拿,第一个方法拿的时候,把获取到的connection放到ThreadLocal中,后面的方法要拿的时候从ThreadLocal中直接拿,不从线程池中拿
    1. 用途:
  15. 声明式事务保证同一个Connection
  16. 切换数据源(用ThreadLocal,当前线程的记录下来,其他人拿到要用的时候从当前线程中直接取)
  17. 理解含义之后灵活运用即可

image.png