侧边栏壁纸
博主头像
程序员の小站博主等级

行动起来,活在当下

  • 累计撰写 51 篇文章
  • 累计创建 35 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

ThreadLocal

Administrator
2024-10-10 / 0 评论 / 0 点赞 / 1 阅读 / 5908 字
温馨提示:
本文最后更新于 2024-10-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. ThreadLocal是什么,能干什么

ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事物ID)与线程关联起来。

TreadLocal能够实现每一个线程都有自己专属的本地变量副本(自己用自己的变量不用麻烦别人,不和其他人共享,人人有份,人各一份)。主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其改为当前线程所存的副本的值从而避免了线程安全问题。比如[8锁案例]中,资源类是使用同一部手机,多个线程抢夺同一部手机,假如人手一份不是天下太平?

2. 常用API

  • get():返回当前线程的词线程局部变量副本中的值
  • initialValue():返回词线程局部变量的当前线程的"初始值"
  • remove():删除词线程局部变量的当前线程的值
  • set(V value):将当前线程的词线程局部变量副本中的值设置为value
  • withInitial(Supplier< ? extends S> supplier): 创建线程的局部变量

3. 案例演示

需求:5个销售卖房子,集团只关心销售总量的精确统计数

class House {
    int saleCount = 0;

    public synchronized void saleHouse() {
        saleCount++;
    }
}
public class ThreadLocalDemo {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(5) + 1;
                System.out.println(size);
                for (int j = 1; j <= size; j++) {
                    house.saleHouse();
                }
            }, String.valueOf(i)).start();

        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t" + "共计卖出多少套: " + house.saleCount);
    }
}

需求变更:希望各自分灶吃饭,各凭销售本事提成,按照出单数各自统计-------比如房产中介销售都有自己的销售额指标,自己专属自己的,不和别人参和

class House {

    int saleCount = 0;

    public synchronized void saleHouse() {
        saleCount++;
    }

    ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);

    public void saleVolumeByThreadLocal() {
        saleVolume.set(1 + saleVolume.get());
    }

}
public class ThreadLocalDemo {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(5) + 1;
                try {
                    for (int j = 1; j <= size; j++) {
                        house.saleHouse();
                        house.saleVolumeByThreadLocal();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + "号销售卖出:" + house.saleVolume.get());
                } finally {
                    house.saleVolume.remove();
                }
            }, String.valueOf(i)).start();

        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t" + "共计卖出多少套: " + house.saleCount);
    }
}

4. Thread、ThreadLocal、ThreadLocalMap关系

img_5.png
ThreadLocalMap实际上就是一个以ThreadLocal实例为Key,任意对象为value的Entry对象。

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

当我们为ThreadLocal变量赋值,实际上就是以当前ThreadLocal实例为Key,值为value的Entry往这个ThreadLocalMap中存放。

5. ThreadLocal内存泄漏

5.1 什么是内存泄漏

不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏

5.2 为什么会出现内存泄漏

img_6.png
ThreadLocalMap从字面上就可以看出这是一个保存ThreadLocal对象的map(以ThreadLocal为Key)。不过是经过了两层包装的ThreadLocal的对象。第一层包装是使用了WeakReference<ThreadLocal< ?>>将ThreadLocal对象变成一个弱引用对象, 第二层包装是定义了一个专门的类Entry来扩展WeakReference<ThreadLocal<?>>。

0

评论区