Java 多线程学习四(ThreadLocal)

本文介绍了Java中的ThreadLocal,它为每个线程提供独立的局部变量副本,实现线程间数据隔离,避免了多线程资源竞争。ThreadLocalMap是其核心,存在内存泄露风险,需注意手动调用remove()。文中还给出了ThreadLocal的代码实例及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、ThreadLocal的概念

ThreadLocal并不是线程,而是Thread的一个局部变量,每个线程都有自己的ThreadLocal变量,可以通过get()set()方法来获取相应值。ThreadLocal设计的初衷是为了解决多线程编程中的资源共享问题,不同于synchronized,ThreadLocal是为每个使用该变量的线程提供独立的变量副本,在各自线程内部,相当于“全局变量”,保证各自线程内操作的是同一个对象,不同于Hashmap数组+链表/红黑树的结构,ThreadLocalmap通过Entry数组保存局部变量,相对于synchronized的“以时间换空间”,ThreadLocal是“以空间换时间”,实现了线程的数据隔离。

二、ThreadLocal原理

源码存在于jdk上,这里不再具体分析,感兴趣可自行探究

  • 每个Thread具有ThreadLocal这各局部变量
  • ThreadLocalMap是ThreadLocal的内部类,通过Entry存储
  • key-value,key是ThreadLocal对象,值是被包装的对象
  • set()方法,往ThreadLocalMap设置值,get()方法获取被包装的对象值

这里需要注意的一点是,ThreadLocalMap的生命周期跟Thread一样,ThreadLocal中使用的 key 为 ThreadLocal的弱引用,而 value 是强引用。所以,如果 ThreadLocal没有被外部强引用的情况下,在垃圾回收的时候会 key 会被清理掉,而 value 不会被清理掉。这样一来,ThreadLocal中就会出现key为null的Entry。导致value不会被GC,造成内存泄露,所以ThreadLocal的remove()方法需要我们手动调用。

三、代码实例

1、创建Person对象,包含age属性,建立ThreadLocal容器对对象Person进行封装,并提供get/set方法。

package ThreadLocalDemo;

import java.util.Random;

//创建类,年龄属性
class Person {
    private int age = 0;

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class MyThreadLocal {
    //创建MyThreadLocal作为容器,将Person对象保存于ThreadLocal中
    public static final ThreadLocal<Person> threadLocal = new ThreadLocal<>();

    public static void set(Person person) {
        threadLocal.set(person);
    }

    //调用remove()方法,防止内存泄漏
    public static void remove() {
        threadLocal.remove();
    }

    public static Person get() {
        return threadLocal.get();
    }
}

public class ThreadLocalDemo1 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Person p = new Person();
                    int age = (new Random()).nextInt(100);
                    p.setAge(age);
                    System.out.println("Person in " + Thread.currentThread().getName() + " set age to " + age);
                    MyThreadLocal.set(p);
                    System.out.println("Person in " + Thread.currentThread().getName() + "'s age is " + MyThreadLocal.get().getAge());
                }
            }).start();
        }
    }
}

2、SimpleDateFormat是线程不安全的

package ThreadLocalDemo;


import java.text.SimpleDateFormat;

public class ThreadLocalDemo2 {
    //SimpleDateFormat非线程安全
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() {
        //重写initialValue方法,赋初始值
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("YYYYMMDDHHMM");
        }
    };

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("before set formatter in " + Thread.currentThread().getName() + " pattern is " + formatter.get().toPattern());
                    formatter.set(new SimpleDateFormat());
                    System.out.println("after set formatter in " + Thread.currentThread().getName() + " pattern is " + formatter.get().toPattern());
                }
            }).start();
        }
    }
}

ThreadLocal应用场景:数据库连接、Session管理等。

未完待续......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值