使用ThreadLocal变量的时机和方法

本文深入探讨了ThreadLocal在并发编程中的作用及其应用场景,特别是在处理多线程环境下唯一事务ID的生成问题。

  并发编程中,一个重要的内容是数据共享。当你创建了实现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 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 threadId =

  new ThreadLocal() {

  @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 startDate =

  new ThreadLocal() {

  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变量会在响应客户端之前被移除,因为当前线程可能被下一个请求重复使用。而 且,如果在使用完毕后不进行清理,它所保持的任何一个对类的引用—这个类会作为部署应用的一部分加载进来—将保留在永久堆栈中,永远不会被垃圾回收机制回收。

文章来源:http://www.jinriwujin.com/

**项目概述:** 本资源提供了一套采用Vue.js与JavaScript技术栈构建的古籍文献文字检测与识别系统的完整源代码及相关项目文档。当前系统版本为`v4.0+`,基于`vue-cli`脚手架工具开发。 **环境配置与运行指引:** 1. **获取项目文件**后,进入项目主目录。 2. 执行依赖安装命令: ```bash npm install ``` 若网络环境导致安装缓慢,可通过指定镜像源加速: ```bash npm install --registry=https://registry.npm.taobao.org ``` 3. 启动本地开发服务器: ```bash npm run dev ``` 启动后,可在浏览器中查看运行效果。 **构建与部署:** - 生成测试环境产物: ```bash npm run build:stage ``` - 生成生产环境优化版本: ```bash npm run build:prod ``` **辅助操作命令:** - 预览构建后效果: ```bash npm run preview ``` - 结合资源分析报告预览: ```bash npm run preview -- --report ``` - 代码质量检查与自动修复: ```bash npm run lint npm run lint -- --fix ``` **适用说明:** 本系统代码经过完整功能验证,运行稳定可靠。适用于计算机科学、人工智能、电子信息工程等相关专业的高校师生、研究人员及开发人员,可用于学术研究、课程实践、毕业设计或项目原型开发。使用者可在现有基础上进行功能扩展或定制修改,以满足特定应用场景需求。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
【EI复现】基于阶梯碳交易的含P2G-CCS耦合燃气掺氢的虚拟电厂优化调度(Matlab代码实现)内容概要:本文介绍了基于阶梯碳交易机制的虚拟电厂优化调度模型,重点研究了包含P2G-CCS(电转气-碳捕集与封存)耦合技术燃气掺氢技术的综合能源系统在Matlab平台上的仿真与代码实现。该模型充分考虑碳排放约束与阶梯式碳交易成本,通过优化虚拟电厂内部多种能源设备的协同运行,提升能源利用效率并降低碳排放。文中详细阐述了系统架构、数学建模、目标函数构建(涵盖经济性与环保性)、约束条件处理及求解方法,并依托YALMIP工具包调用求解器进行实例验证,实现了科研级复现。此外,文档附带网盘资源链接,提供完整代码与相关资料支持进一步学习与拓展。; 适合人群:具备一定电力系统、优化理论及Matlab编程基础的研究生、科研人员或从事综合能源系统、低碳调度方向的工程技术人员;熟悉YALMIP常用优化算法者更佳。; 使用场景及目标:①学习复现EI级别关于虚拟电厂低碳优化调度的学术论文;②掌握P2G-CCS、燃气掺氢等新型低碳技术在电力系统中的建模与应用;③理解阶梯碳交易机制对调度决策的影响;④实践基于Matlab/YALMIP的混合整数线性规划或非线性规划问题建模与求解流程。; 阅读建议:建议结合提供的网盘资源,先通读文档理解整体思路,再逐步调试代码,重点关注模型构建与代码实现之间的映射关系;可尝试修改参数、结构或引入新的约束条件以深化理解并拓展应用场景。
ThreadLocal适用于以下几种使用时机: ### 对象隔离 当多个线程需要使用同一个对象,但该对象不是线程安全的,为了避免线程安全问题,可以使用ThreadLocal为每个线程创建一个独立的对象副本。例如在处理日期格式化时,`SimpleDateFormat` 不是线程安全的,使用ThreadLocal可以为每个线程分配自己的 `SimpleDateFormat` 对象,保证线程安全并高效利用内存。示例代码如下: ```java class ThreadSafeFormatter { // lambda表达式写法 public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); } SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get(); ``` 上述代码利用ThreadLocal,给每个线程分配自己的 `dateFormat` 对象,保证了线程安全 [^2]。 ### 线程关联状态存储 在一个类中,ThreadLocal类型的实例通常作为私有、静态字段,可以将其作为线程的关联状态,如用户ID或者事务ID。每个线程可以通过 `get` 或 `set` 方法访问其自身所拥有的、独立初始化的变量拷贝 [^5]。示例代码如下: ```java import java.util.UUID; public class UserContext { private static final ThreadLocal<String> USER_ID_THREAD_LOCAL = new ThreadLocal<>(); public static void setUserId(String userId) { USER_ID_THREAD_LOCAL.set(userId); } public static String getUserId() { return USER_ID_THREAD_LOCAL.get(); } public static void clearUserId() { USER_ID_THREAD_LOCAL.remove(); } public static void main(String[] args) { Thread t1 = new Thread(() -> { String userId = UUID.randomUUID().toString(); setUserId(userId); System.out.println("Thread 1 User ID: " + getUserId()); clearUserId(); }); Thread t2 = new Thread(() -> { String userId = UUID.randomUUID().toString(); setUserId(userId); System.out.println("Thread 2 User ID: " + getUserId()); clearUserId(); }); t1.start(); t2.start(); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值