千家信息网

Java中ThreadLocal如何使用

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇文章将为大家详细讲解有关Java中ThreadLocal如何使用,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。从源码入手首先,让我们看看 Thre
千家信息网最后更新 2025年02月01日Java中ThreadLocal如何使用

这篇文章将为大家详细讲解有关Java中ThreadLocal如何使用,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

从源码入手

首先,让我们看看 ThreadLocal 类中的介绍: >This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). > >Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

按照文中所述,ThreadLocal 提供的是线程本地变量,每个线程都有一份单独的副本,经常使用的方式是私有静态变量。关键在于下一段,线程存活,ThreadLocal 实例就可以被访问,线程消失,就会被垃圾回收。

get()方法

看到这儿,有没有想起上一篇内容所说的引用类型,有可能是软引用或者弱引用,具体是什么呢?还是来看看代码:

    public T get() {        // 获取当前线程        Thread t = Thread.currentThread();        // 获取线程里的map        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }

上面展示的是 ThreadLocal 中的get()方法,关键的 map 是在 Thread 类中的threadLocals变量,让我们继续看看 ThreadLocalMap 的源代码:

    ThreadLocal.ThreadLocalMap threadLocals = null;    static class ThreadLocalMap {        static class Entry extends WeakReference> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal k, Object v) {                // 使用ThreadLocal作为key,并且是弱引用                super(k);                value = v;            }        }        // 省略代码    }

根据上一篇文章所述,如果一个对象只有弱引用,那么当下一次 GC 进行时,该对象就会被回收。那么让我们整理一下:

  1. ThreadLocalMap 的 Entry 对 ThreadLocal 的引用为弱引用

  2. ThreadLocal 本身并不存储值,具体的 value 依旧在各个线程中。因此你可以把 ThreadLocal 看成一个工具类。

但需要注意的是,Entry 中,只有key是弱引用,但 value 依旧是强引用。那会不会出现 key 被垃圾回收后,这个 map 的 key 为 null,但 value 依旧存在的情况呢?

set()方法

确实是有可能的,但 JDK 本身也做了优化,可以看看 ThreadLocalMap 的 set()方法:

        private void set(ThreadLocal key, Object value) {            // We don't use a fast path as with get() because it is at            // least as common to use set() to create new entries as            // it is to replace existing ones, in which case, a fast            // path would fail more often than not.            Entry[] tab = table;            int len = tab.length;            int i = key.threadLocalHashCode & (len-1);            for (Entry e = tab[i];                 e != null;                 e = tab[i = nextIndex(i, len)]) {                ThreadLocal k = e.get();                if (k == key) {                    e.value = value;                    return;                }                if (k == null) {                    replaceStaleEntry(key, value, i);                    return;                }            }            tab[i] = new Entry(key, value);            int sz = ++size;            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();        }

调用 set()的时候,ThreadLocalMap 检查到 key 为 null 的 entry 时,会将 value 也设置为 null,这样 value 之前对应的实例也可以被回收。

使用场景

简单使用

先让我们看一个简单的例子:

public class ThreadLocalSimpleDemo {    public static void main(String[] args) {        int threads = 3;        InnerClass innerClass = new InnerClass();        for (int i = 1; i <= threads; i++) {            new Thread(() -> {                for (int j = 0; j < 4; j++) {                    innerClass.add(String.valueOf(j));                    innerClass.print();                }                innerClass.set("hello world");            }, "thread - " + i).start();        }    }    private static class InnerClass {        /**         * 添加         */        public void add(String newStr) {            StringBuilder str = Counter.counter.get();            Counter.counter.set(str.append(newStr));        }        /**         * 打印         */        public void print() {            System.out.printf(                    "Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n",                    Thread.currentThread().getName(),                    Counter.counter.hashCode(),                    Counter.counter.get().hashCode(),                    Counter.counter.get().toString()            );        }        /**         * 赋值         */        public void set(String words) {            Counter.counter.set(new StringBuilder(words));            System.out.printf(                    "Set, Thread name:%s , ThreadLocal hashcode:%s,  Instance hashcode:%s, Value:%s\n",                    Thread.currentThread().getName(),                    Counter.counter.hashCode(),                    Counter.counter.get().hashCode(),                    Counter.counter.get().toString()            );        }    }    private static class Counter {        /**         * 初始化时是一个空的StringBuilder对象         */        private static ThreadLocal counter = ThreadLocal.withInitial(StringBuilder::new);    }}

其打印结果为:

Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:01Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:012Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0123Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:01Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:012Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0123Set, Thread name:thread - 1 , ThreadLocal hashcode:310471657,  Instance hashcode:820066274, Value:hello worldThread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:01Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:012Set, Thread name:thread - 2 , ThreadLocal hashcode:310471657,  Instance hashcode:155293473, Value:hello worldThread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0123Set, Thread name:thread - 3 , ThreadLocal hashcode:310471657,  Instance hashcode:1804272849, Value:hello world

可以看出,我们在使用 ThreadLocal 时,用的是同一个对象,但各个线程对应的实例是不一样的。而在调用 set() 方法后,对应的实例会被替换。

Session

对于 Java Web 应用而言,Session 保存了很多信息。很多时候需要通过 Session 获取信息,有些时候又需要修改 Session 的信息。一方面,需要保证每个线程有自己单独的 Session 实例。另一方面,由于很多地方都需要操作 Session,存在多方法共享 Session 的需求。使用 ThreadLocal 进行实现:

public class SessionHandler {  public static ThreadLocal session = ThreadLocal.withInitial(() -> new Session());  @Data  public static class Session {    private String id;    private String user;    private String status;  }  public String getUser() {    return session.get().getUser();  }  public String getStatus() {    return session.get().getStatus();  }  public void setStatus(String status) {    session.get().setStatus(status);  }  public static void main(String[] args) {    new Thread(() -> {      SessionHandler handler = new SessionHandler();      handler.getStatus();      handler.getUser();      handler.setStatus("close");      handler.getStatus();    }).start();  }}

关于Java中ThreadLocal如何使用就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

线程 实例 方法 对象 信息 内容 变量 时候 篇文章 代码 关键 只有 垃圾 文章 更多 知识 上一 不错 一方 例子 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件工程对网络安全的重要性 网络安全甲方乙方区分 网络安全法律法规宣贯主题 模拟城市下载软件开发 网络技术学院考研方向 苏州oa软件开发工程师 数据库备份训练营 深圳深工网络技术有限公司 软件开发行业的标准 金山软件网络安全 网络安全的博弈 智慧记提示数据库已损坏 饥荒联机版加服务器mod 电力网络安全事件应急演练 支撑直播的正式网络技术 绍兴软件开发驻场代理公司 为什么学校用宽带找不到服务器 虎丘区营销网络技术有哪些 徐州网络软件开发哪个好 软件开发项目的实施方案 计算机网络技术面试说什么 新都区哪里招聘网络安全工程师 市北区ios软件开发公司 森林资源空间数据库建设规程 中国校园服务器地址 h5软件开发公司有哪些 软件开发对比销售 公安部网络安全流量 软件企业购买服务器几年计提折旧 宿迁江苏高性能服务器高性价比
0