首页 > 其他分享 >使用ThreadLocal

使用ThreadLocal

时间:2024-02-02 09:00:26浏览次数:29  
标签:threadLocalUser void ThreadLocal 线程 user 使用 User

线程是Java实现多任务的基础,Thread对象代表一个线程,我们可以在代码中调用Thread.currentThread()获取当前线程。例如,打印日志时,可以同时打印出当前线程的名字:

 

 

对于多任务,Java标准库提供的线程池可以方便地执行这些任务,同时复用线程。Web应用程序就是典型的多任务应用,每个用户请求页面时,我们都会创建一个任务,类似:

public void process(User user) {
    checkPermission();
    doWork();
    saveStatus();
    sendResponse();
}

然后,通过线程池去执行这些任务。

观察process()方法,它内部需要调用若干其他方法,同时,我们遇到一个问题:如何在一个线程内传递状态?

process()方法需要传递的状态就是User实例。有的童鞋会想,简单地传入User就可以了:

public void process(User user) {
    checkPermission(user);
    doWork(user);
    saveStatus(user);
    sendResponse(user);
}

但是往往一个方法又会调用其他很多方法,这样会导致User传递到所有地方:

void doWork(User user) {
    queryStatus(user);
    checkStatus();
    setNewStatus(user);
    log();
}

这种在一个线程中,横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等。

给每个方法增加一个context参数非常麻烦,而且有些时候,如果调用链有无法修改源码的第三方库,User对象就传不进去了。

Java标准库提供了一个特殊的ThreadLocal,它可以在一个线程中传递同一个对象。

ThreadLocal实例通常总是以静态字段初始化如下:

static ThreadLocal<User> threadLocalUser = new ThreadLocal<>();

它的典型使用方式如下:

void processUser(user) {
    try {
        threadLocalUser.set(user);
        step1();
        step2();
    } finally {
        threadLocalUser.remove();
    }
}

通过设置一个User实例关联到ThreadLocal中,在移除之前,所有方法都可以随时获取到该User实例:

void step1() {
    User u = threadLocalUser.get();
    log();
    printUser();
}

void log() {
    User u = threadLocalUser.get();
    println(u.name);
}

void step2() {
    User u = threadLocalUser.get();
    checkUser(u.id);
}

通过设置一个User实例关联到ThreadLocal中,在移除之前,所有方法都可以随时获取到该User实例:

void step1() {
    User u = threadLocalUser.get();
    log();
    printUser();
}

void log() {
    User u = threadLocalUser.get();
    println(u.name);
}

void step2() {
    User u = threadLocalUser.get();
    checkUser(u.id);
}

注意到普通的方法调用一定是同一个线程执行的,所以,step1()step2()以及log()方法内,threadLocalUser.get()获取的User对象是同一个实例。

实际上,可以把ThreadLocal看成一个全局Map<Thread, Object>:每个线程获取ThreadLocal变量时,总是使用Thread自身作为key:

Object threadLocalValue = threadLocalMap.get(Thread.currentThread());

因此,ThreadLocal相当于给每个线程都开辟了一个独立的存储空间,各个线程的ThreadLocal关联的实例互不干扰。

最后,特别注意ThreadLocal一定要在finally中清除:

try {
    threadLocalUser.set(user);
    ...
} finally {
    threadLocalUser.remove();
}

 

 

小结

ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的;

ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);

使用ThreadLocal要用try ... finally结构,并在finally中清除。

 

 

 

 

 

参考  https://www.liaoxuefeng.com/wiki/1252599548343744/1306581251653666

标签:threadLocalUser,void,ThreadLocal,线程,user,使用,User
From: https://www.cnblogs.com/JavaYuYin/p/18002506

相关文章

  • pytorch的模型推理:TensorRT的使用
    相关教程视频:TRTorch真香,一键启用TensorRT图片来源:https://www.bilibili.com/video/BV1TY411h7xC/图片来源:https://www.bilibili.com/video/BV1TY411h7xC/......
  • [香橙派开发系列]使用蓝牙和手机进行信息的交换
    目录前言一、HC05蓝牙模块1.HC05概述2.HC05的连接图3.进入HC05的命令模式4.常用的AT指令4.1检查AT是否上线4.2重启模块4.3获取软件版本号4.4恢复默认状态4.5获取蓝牙的名称4.6设置蓝牙模块的波特率4.7查询蓝牙的连接模式4.8查询模块角色5.连接电脑6.通过HC05发送消息7.stm......
  • 离线解锁 CodeCombat 全关卡教程 使用docker安装实现
    前期准备下载安装dockerdesktophttps://www.123pan.com/s/fmvUVv-HqApH,这个安装不会的随便搜一个教程,挺多的。我随便找了一个知乎的Windows10Docker安装详细教程下载数据dump.tar.gzhttps://www.123pan.com/s/fmvUVv-hqApH开始打开cmd拉镜像dockerpulloper......
  • mozhe靶场: WebShell文件上传漏洞分析溯源(第5题) 题解(使用哥斯拉)
    哥斯拉由java编写,可以在linux上使用.个人认为比冰蝎好用,用冰蝎连不上这个靶场,但是哥斯拉可以连的上.github搜哥斯拉就能下载首先登陆后台,弱口令adminadmin点击添加文章,尝试上传一句话木马(一句话木马可以点击哥斯拉的生成)webshell.asp<%evalrequest("pass")%>......
  • UniGUI使用ADO组件、调用数据库的存储过程、生成EXECL表的例子
    UniGUI使用ADO组件、调用数据库的存储过程、生成EXECL表的例子(自己学习记录一下,不一定合理,仅供参考)本例子是使用ADO等组件连接一个云服务器的一个数据库,调用GetOrg存储过程,把机构信息展现把结果导出的一个EXECL表里,并下载把显示的HSate的值进行替换1表示正常,其他表示暂停......
  • Go语言精进之路读书笔记第12条——使用复合字面值作为初值构造器
    有些时候,零值并非最好的选择,我们有必要为变量赋予适当的初值以保证其后续以正确的状态参与业务流程计算,尤其是Go语言中的一些复合类型的变量。Go提供了复合字面值(compositeliteral)语法可以作为复合类型变量的初值构造器。Go语言中的复合类型包括结构体、数组/切片和map。Go提供......
  • Go语言精进之路读书笔记第9条——使用无类型常量简化代码
    9.1Go常量溯源绝大多数情况下,Go常量在声明时并不显式指定类型,也就是说使用的是无类型常量(untypedconstant)。9.2有类型常量带来的烦恼如果有类型常量与变量的类型不同,那么混合运算的求值操作会报错:typemyIntintconstnmyInt=13//constmint=n+5//编译器错误提......
  • Go语言精进之路读书笔记第10条——使用iota实现枚举常量
    Go的const语法提供了隐式重复前一个非空表达式的机制const(Apple,Banan=11,22Straberry,GrapePear,Watermelon)//等价于const(Apple,Banan=11,22Straberry,Grape=11,22Pear,Watermelon=11,22)iota是Go语言的一个预定义......
  • Go语言精进之路读书笔记第8条——使用一致的变量声明形式
    Go语言常见的变量声明形式varaint32varsstring="hello"vari=13n:=17var(crlf=[]byte("\r\n")colonSpace=[]byte(":"))8.1包级变量的声明形式1.声明并同时显式初始化//Go编译器会自动根据等号右侧的InitExpression表达式求值的类型确定左侧......
  • 使用crontab定期下载经济学人audio
    如果不确定自己的crontab时间参数设置的是否正确,可以参照这里:https://crontab.guru/更多crontab设置的问题,也可以参照这里:https://stackoverflow.com/questions/31260837/how-to-run-a-cron-job-on-every-monday-wednesday-and-friday  #runat7o'clockeveryFriday#A......