ThreadLocal详解


一、ThreadLocal简介

  • ThreadLocal 叫做线程变量,意思是 ThreadLocal 中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal 为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
    • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
    • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

二、手写ThreadLocal模拟应用

1.MyThreadLocal

package com.bdg.threadlocal;

import java.util.HashMap;
import java.util.Map;
/**
 * 自定义一个ThreadLocal类
 */
public class MyThreadLocal<T> {
   
    /**
     * 所有需要和当前线程绑定的数据都要放到这个容器中
     */
    private final Map<Thread, T> map = new HashMap<>();

    /**
     * 向ThreadLocal中绑定元素
     * @param obj
     */
    public void set(T obj) {
   
        map.put(Thread.currentThread(), obj);
    }

    /**
     * 从ThreadLocal中取元素
     * @return
     */
    public T get() {
   
        return map.get(Thread.currentThread());
    }

    /**
     * 移除ThreadLocal中的元素
     */
    public void remove() {
   
        map.remove(Thread.currentThread());
    }
}

2.模拟连接对象Connection并编写工具类

(1)Connection

package com.bdg.threadlocal;

public class Connection {
   }

(2)DBUtil

package com.bdg.threadlocal;

public class DBUtil {
   
    //静态变量类加载时执行,并且只执行一次
    //全局的大map
    /*所有需要与线程绑定的数据的大容器*/
    private static final MyThreadLocal<Connection> local = new MyThreadLocal<>();

    /**
     * 每一次都调用这个方法来获取Connection对象
     */
    public static Connection getConnection() {
   
        Connection conn = local.get();
        if (conn == null) {
   
            //第一次调用getConnection方法时conn一定是null.
            //空的就需要new
            conn = new Connection();
            //将new出来的conn对象绑定到大map中
            local.set(conn);
        }
        return conn;
    }
}

3.编写程序使用自己编写的MyThreadLocal

(1)UserDao

package com.bdg.threadlocal;

public class UserDao {
   
    public void insert() {
   
        Thread thread = Thread.currentThread();
        System.out.println(thread);

        Connection conn = DBUtil.getConnection();
        System.out.println(conn);

        System.out.println("User DAO insert");
    }
}

(2)UserService

package com.bdg.threadlocal;

public class UserService {
   

    private UserDao userDao = new UserDao();
    public void save() {
   
        Thread thread = Thread.currentThread();
        System.out.println(thread);

        Connection conn = DBUtil.getConnection();
        System.out.println(conn);

        userDao.insert();
    }
}

(3)Test

package com.bdg.threadlocal;

public class Test {
   
    public static void main(String[] args) {
   

        Thread thread = Thread.currentThread();
        System.out.println(thread);

        //调用service
        UserService userService = new UserService();
        userService.save();
    }
}

4.结果展示

在这里插入图片描述


三、ThreadLocal的应用场景

  • ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景
    • 1.存储用户 Session
    • 2.数据库连接,处理数据库事务
    public class DBUtil {
         
    
        private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc");
        private static String driver = bundle.getString("driver");
        private static String url = bundle.getString("url");
        private static String user = bundle.getString("user");
        private static String password = bundle.getString("password");
        private static ThreadLocal<Connection> local = new ThreadLocal<>();
        //不然创建对象,因为工具类中的方法都是静态的,不需要创建对象。
        //为了防止创建对象,故将构造方法私有化
        private DBUtil() {
         }
    
        //DBUtil类加载时注册驱动
        static {
         
            try {
         
                Class.forName(driver);
            } catch (ClassNotFoundException e) {
         
                e.printStackTrace();
            }
        }
    
        //这里没有使用数据库连接池,直接创建连接对象
        public static Connection getConnection() throws SQLException {
         
            Connection conn = local.get();
            if (conn == null) {
         
                conn = DriverManager.getConnection(url, user, password);
                local.set(conn);
            }
            System.out.println(conn);
            return conn;
        }
    
        public static void close (Connection conn, Statement stmt, ResultSet rs) {
         
            if (conn != null) {
         
                try {
         
                    conn.close();
                    //Tomcat是支持多线程的,假设张三使用了T1连接对象,其他的用户也可能使用T1连接对象
                    local.remove();
                } catch (SQLException e) {
         
                    throw new RuntimeException(e);
                }
            }
            if (stmt != null) {
         
                try {
         
                    stmt.close();
                } catch (SQLException e) {
         
                    throw new RuntimeException(e);
                }
            }
            if (rs != null) {
         
                try {
         
                    rs.close();
                } catch (SQLException e) {
         
                    throw new RuntimeException(e);
                }
            }
        }
     }
    
    • 3.数据跨层传递(controller、service、dao)
      • 上面手写 ThreadLocal 示例中处理了跨层访问的问题。
    • 4.Spring 使用 ThreadLocal 解决线程安全问题

三、总结

  • 通过上面的模拟我们可以看出,ThreadLocal 其实就是一个全局的大 Map,ThreadLocal 的 key 部分存储的是线程对象,我们可以将所有需要和当前线程绑定的数据都要放到这个容器中。
  • 在 JDK 中给我们内置了 ThreadLocal ,不需要我们自己手动来写了,可以直接使用。

1.如何正确的使用ThreadLocal

  • ① 将 ThreadLocal 变量定义成 private static 的,这样的话 ThreadLocal 的生命周期就更长,由于一直存在 ThreadLocal 的强引用,所以 ThreadLocal 也就不会被回收,也就能保证任何时候都能根据 ThreadLocal 的弱引用访问到 Entry 的 value 值,然后 remove 它,防止内存泄露
  • ② 每次使用完 ThreadLocal,都调用它的 remove() 方法,清除数据。

相关推荐

  1. ThreadLocal

    2024-02-20 05:42:02       38 阅读
  2. ThreadLocal

    2024-02-20 05:42:02       21 阅读
  3. ThreadLocal介绍

    2024-02-20 05:42:02       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-20 05:42:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-20 05:42:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-20 05:42:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-20 05:42:02       20 阅读

热门阅读

  1. 搜索+哈希/平衡树,LeetCode 987. 二叉树的垂序遍历

    2024-02-20 05:42:02       33 阅读
  2. 大数据经责审计

    2024-02-20 05:42:02       34 阅读
  3. the file size exceeds the configured limit Android studio

    2024-02-20 05:42:02       24 阅读
  4. c++ %运算符

    2024-02-20 05:42:02       26 阅读
  5. 2024前端面试准备之Vue3篇

    2024-02-20 05:42:02       36 阅读
  6. docker的底层原理一:客户端-服务器架构

    2024-02-20 05:42:02       31 阅读
  7. LeetCode--2298. 周末任务计数

    2024-02-20 05:42:02       29 阅读