关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »使用ThreadLocal变量的时机和方法

    使用ThreadLocal变量的时机和方法

    2015-02-04 00:00:00 出处:InfoQ - 黄玲艳
    分享

    并发编程中,一个重要的内容是数据共享。当你创建了实现Runnable接口的线程,然后开启使用相同Runnable实例的各种Thread对象,所有 的线程便共享定义在Runnable对象中的属性。也就是说,当你在一个线程中改变任意属性时,所有的线程都会因此受到影响,同时会看到第一个线程修改后的值。有时我们希望如此,比如:多个线程增大或减小同一个计数器变量;但是,有时我们希望确保每个线程,只能工作在它自己的线程实例的拷贝上,同时不会影 响其他线程的数据。

    使用ThreadLocal的时机

    举个例子,想象你在开发一个电子商务应用,你需要为每一个控制器处理的顾客请求,生成一个唯一的事务ID,同时将其传到管理器或DAO的业务方法中,以便记录日志。一种方案是将事务ID作为一个参数,传到所有的业务方法中。但这并不是一个好的方案,它会使代码变得冗余。

    你可以使用ThreadLocal类型的变量解决这个问题。首先在控制器或者任意一个预处理器拦截器中生成一个事务ID,然后在ThreadLocal中 设置事务ID,最后,不论这个控制器调用什么方法,都能从threadlocal中获取事务ID。而且这个应用的控制器可以同时处理多个请求,同时在框架 层面,因为每一个请求都是在一个单独的线程中处理的,所以事务ID对于每一个线程都是唯一的,而且可以从所有线程的执行路径获取。

    扩展阅读:与JAX-RS ResteasyProviderFactory共享上下文数据(ThreadLocalStack实例)

    ThreadLocal类

    Java并发API为使用ThreadLocal类的局部线程变量提供了一个简洁高效的机制,

    public class ThreadLocal<T> extends Object {...}

    这个类提供了一个局部线程变量。这些变量不同于其所对应的常规变量,对于常规变量,每个线程只能访问(通过get或set方法)其自身所拥有的,独立初始化变量拷贝。在一个类中,ThreadLocal类型的实例是典型的私有、静态(private static)字段,因为我们可以将其作为线程的关联状态(比如:用户ID或者事务ID)

    这个类有以下方法:

    get():返回当前线程拷贝的局部线程变量的值。 initialValue():返回当前线程赋予局部线程变量的初始值。 remove():移除当前线程赋予局部线程变量的值。 set(T value):为当前线程拷贝的局部线程变量设置一个特定的值。

    怎样使用ThreadLocal?

    下面的例子使用两个局部线程变量,即threadId和startDate。它们都遵循推荐的定义方法,即“private static”类型的字段。threadId用来区分当前正在运行的线程,startDate用来获取线程开启的时间。上面的信息将打印到控制台,以此验 证每一个线程管理他自己的变量拷贝。

    class DemoTask implements Runnable {
    
       // Atomic integer containing the next thread ID to be assigned
       private static final AtomicInteger nextId = new AtomicInteger(0);
    
       // Thread local variable containing each thread's ID
       private static final ThreadLocal<Integer> threadId =
            new ThreadLocal<Integer>() {
                @Override
                protected Integer initialValue() {
                   return nextId.getAndIncrement();
                }
             };
    
       // Returns the current thread's unique ID, assigning it if necessary
       public int getThreadId() {
          return threadId.get();
       }
    
       // Returns the current thread's starting timestamp
       private static final ThreadLocal<Date> startDate =
           new ThreadLocal<Date>() {
               protected Date initialValue() {
                   return new Date();
               }
           };
    
       @Override
       public void run() {
          System.out.printf("Starting Thread: %s : %sn",
                            getThreadId(), startDate.get());
          try {
             TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
          } catch (InterruptedException e) {
             e.printStackTrace();
          }
          System.out.printf("Thread Finished: %s : %sn",
                            getThreadId(), startDate.get());
       }
    }

    现在要验证变量本质上能够维持其自身状态,而与多线程的多次初始化无关。我们首先需要创建执行这个任务的三个线程,然后开启线程,接着验证它们打印到控制台中的信息。

    Starting Thread: 0 : Wed Dec 24 15:04:40 IST 2014
    Thread Finished: 0 : Wed Dec 24 15:04:40 IST 2014
    
    Starting Thread: 1 : Wed Dec 24 15:04:42 IST 2014
    Thread Finished: 1 : Wed Dec 24 15:04:42 IST 2014
    
    Starting Thread: 2 : Wed Dec 24 15:04:44 IST 2014
    Thread Finished: 2 : Wed Dec 24 15:04:44 IST 2014

    在上面的输出中,打印出的声明序列每次都在变化。我已经把它们放到了序列中,这样对于每一个线程实例,我们都可以清楚地辨别出,局部线程变量保持着安全状态,而绝不会混淆。自己尝试下!

    局部线程通常使用在这样的情况下,当你有一些对象并不满足线程安全,但是你想避免在使用synchronized关键字、块时产生的同步访问,那么,让每个线程拥有它自己的对象实例。

    注意:局部变量是同步或局部线程的一个好的替代,它总是能够保证线程安全。唯一可能限制你这样做的是你的应用设计约束。

    警告:在webapp服务器上,可能会保持一个线程池,那么ThreadLocal变量会在响应客户端之前被移除,因为当前线程可能被下一个请求重复使用。而 且,如果在使用完毕后不进行清理,它所保持的任何一个对类的引用—这个类会作为部署应用的一部分加载进来—将保留在永久堆栈中,永远不会被垃圾回收机制回收。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    如何从 Windows 11 中完全删除/卸载 OneNote?无解!

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    IT头条

    华为Pura80系列新机预热,余承东力赞其复杂光线下的视频拍摄实力

    01:28

    阿里千问3开源首战告捷:全球下载破千万,国产AI模型崛起新高度!

    01:22

    DeepSeek R1小版本试升级:网友实测编程能力已达到国际一线水平

    23:15

    NVIDIA 与 Dell 合作,大规模交付 Blackwell AI 系统

    20:52

    Cerebras 以最快的 Llama 4 Maverick 性能引领 LLM 推理竞赛

    20:51

    技术热点

    PHP中的随机性——你觉得自己幸运吗?

    搞定Ubuntu Linux下WPA无线上网

    Java使用内存映射实现大文件的上传

    MySQL安全性指南

    MySQL两项性能的基本测试浅谈

    教您使用UniqueIdentifier选取SQL Server主键

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。