c) 解决线程安全问题:ThreadLocal
1.ThreadLocal 介绍
ThreadLocal
从字面的意思来理解是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能使用自己的变量。
以上面线程池格式化时间为例,当线程池中有 10 个线程时,SimpleDateFormat
会存入 ThreadLocal
中,它也只会创建 10 个对象,即使要执行 1000 次时间格式化任务,依然只会新建 10 个 SimpleDateFormat
对象,每个线程调用自己的 ThreadLocal
变量。
2.ThreadLocal 基础使用
ThreadLocal
常用的核心方法有三个:
- set 方法:用于设置线程独立变量副本。没有 set 操作的 ThreadLocal 容易引起脏数据。
- get 方法:用于获取线程独立变量副本。没有 get 操作的 ThreadLocal 对象没有意义。
- remove 方法:用于移除线程独立变量副本。没有 remove 操作容易引起内存泄漏。
ThreadLocal 所有方法如下图所示:
官方说明文档:https://docs.oracle.com/javase/8/docs/api/
ThreadLocal 基础用法如下:
/** * @公众号:Java中文社群 */ publicclass ThreadLocalExample { // 创建一个 ThreadLocal 对象 privatestatic ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { // 线程执行任务 Runnable runnable = new Runnable() { @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " 存入值:" + threadName); // 在 ThreadLocal 中设置值 threadLocal.set(threadName); // 执行方法,打印线程中设置的值 print(threadName); } }; // 创建并启动线程 1 new Thread(runnable, "MyThread-1").start(); // 创建并启动线程 2 new Thread(runnable, "MyThread-2").start(); } /** * 打印线程中的 ThreadLocal 值 * @param threadName 线程名称 */ private static void print(String threadName) { try { // 得到 ThreadLocal 中的值 String result = threadLocal.get(); // 打印结果 System.out.println(threadName + " 取出值:" + result); } finally { // 移除 ThreadLocal 中的值(防止内存溢出) threadLocal.remove(); } } }
以上程序的执行结果为:
从上述结果可以看出,每个线程只会读取到属于自己的 ThreadLocal
值。