Java 并发编程:ThreadLocal 简单介绍

Java 并发编程:ThreadLocal 简单介绍


1. ThreadLocal 是如何保证线程安全的?

简单来说,造成线程不安全的原因是多个线程同时去更新共享数据,处理共享数据常用的方法就是加锁,通过加锁的方式来控制线程对共享数据的访问,例如乐观锁和悲观锁。

ThreadLocal 保证线程安全的方式是为每个线程提供一个独立的变量副本来解决冲突问题,每个线程更改的都是自己的变量副本,从根本上解决解决问题。

2. ThreadLocal 使用场景

ThreadLocal 对于每个线程都需要拥有自己的一份本地变量副本的情况下是最为适合的,常用于 Session 会话或数据库连接等。

阿里编码规约上亦有通过 ThreadLocal 保证线程安全性的案例,在第一章第六节并发安全处理部分,用于 SimpleDateFormat 做格式化处理部分。

2ZiERuK7UqXz6sb3. ThreadLocal 简单使用

public class ThreadLocalTest {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new RunnableImpl()).start();
        }
    }

    static class RunnableImpl implements Runnable {

        @Override
        public void run() {
            try {
                // 1、获取随机数
                int random = (int) Math.random() * 1000;

                // 2、设置初始值
                threadLocal.set(random);

                // 3、等待其他线程进行更新
                Thread.sleep(3000);

                // 4、获取初始值
                System.out.println("存取内容是否一致:" + (random == threadLocal.get()));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 不需要用到数据时进行手动删除,避免引起内存泄漏
                threadLocal.remove();
            }
        }
    }
    
}

运行结果:

image-20211020195722114

4. ThreadLocal 解析

// 常用于创建 ThreadLocal 对象同时设置初始值
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    return new SuppliedThreadLocal<>(supplier);
}

// 设置初始值为0,默认为 null
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
// set 方法用于设置初始值,这里是根据线程存放数据
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
public T get() {
    // 获取当前线程并拿到当前线程的 ThreadLocalMap 引用
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // 若引用不为 null 则代表已实例化,可以获取内部属性
    if (map != null) {
        // 获取内部类实例,Entry为ThreadLocalMap的内部类
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            // 获取Entry对象的value属性,value为set方法设置的值
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 进行初始化
    return setInitialValue();
}
/**
 * 当不需要对象副本时请执行remove操作,避免内存溢出
 * static class Entry extends WeakReference<ThreadLocal<?>>
 * ThreadLocalMap.Entry 实例 key 为弱引用,垃圾回收时无论内存是否充足都会被回收
 * 此时 value 作为强引用未能进行回收但已无法通过 ThreadLocal 获取该数据
 */
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

image-20211020202858525

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值