了解ThreadLocal背后的概念

介绍

我知道本地线程,但直到最近才真正使用过它。
因此,我开始深入研究该主题,因为我需要一种传播某些用户信息的简便方法
通过Web应用程序的不同层,而无需更改每个调用方法的签名。

小前提信息

线程是具有自己的调用栈的单个进程。在Java中,每个调用栈有一个线程,或者每个线程有一个调用栈。即使您没有在程序中创建任何新线程,线程也可以在没有您的程序的情况下运行最好的例子是当您仅通过main方法启动一个简单的Java程序时,您没有隐式调用new Thread()。start(),而是JVM为您创建了一个主线程来运行main方法。

主线程是非常特殊的,因为它是所有其他线程都会从中生成的线程,
线程完成后,应用程序结束了它的生命周期。

在Web应用程序服务器中,通常会有一个线程池,因为要创建的线程类非常重。所有JEE服务器(Weblogic,Glassfish,JBoss等)都有一个自调整线程池,这意味着线程池会增加或减少需要的时间,因此不会在每个请求上创建线程,而现有的线程将被重用。

了解线程局部

为了更好地理解线程本地,我将展示一种自定义线程本地的非常简单的实现。

package ccs.progest.javacodesamples.threadlocal.ex1;import java.util.HashMap;
import java.util.Map;public class CustomThreadLocal {private static Map threadMap = new HashMap();public static void add(Object object) {threadMap.put(Thread.currentThread(), object);}public static void remove(Object object) {threadMap.remove(Thread.currentThread());}public static Object get() {return threadMap.get(Thread.currentThread());}}

因此,您可以随时在应用程序中调用CustomThreadLocal上的add方法, 它将把当前线程作为并将要与该线程关联的对象作为值放入映射中 。 该对象可能是您希望从当前执行的线程中的任何位置访问的对象,或者可能是您想要与该线程保持关联并重复使用多次的昂贵对象。
您定义一个ThreadContext类,您在其中拥有要在线程内传播的所有信息。

package ccs.progest.javacodesamples.threadlocal.ex1;public class ThreadContext {private String userId;private Long transactionId;public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public Long getTransactionId() {return transactionId;}public void setTransactionId(Long transactionId) {this.transactionId = transactionId;}public String toString() {return 'userId:' + userId + ',transactionId:' + transactionId;}}

现在是时候使用ThreadContext了。

我将启动两个线程,并在每个线程中添加一个新的ThreadContext实例,该实例将保存我想为每个线程传播的信息。

package ccs.progest.javacodesamples.threadlocal.ex1;public class ThreadLocalMainSampleEx1 {public static void main(String[] args) {new Thread(new Runnable() {public void run() {ThreadContext threadContext = new ThreadContext();threadContext.setTransactionId(1l);threadContext.setUserId('User 1');CustomThreadLocal.add(threadContext);//here we call a method where the thread context is not passed as parameterPrintThreadContextValues.printThreadContextValues();}}).start();new Thread(new Runnable() {public void run() {ThreadContext threadContext = new ThreadContext();threadContext.setTransactionId(2l);threadContext.setUserId('User 2');CustomThreadLocal.add(threadContext);//here we call a method where the thread context is not passed as parameterPrintThreadContextValues.printThreadContextValues();}}).start();}
}

注意:
CustomThreadLocal.add(threadContext)是当前线程与ThreadContext实例相关联的代码行
您将看到执行此代码,结果将是:

userId:User 1,transactionId:1
userId:User 2,transactionId:2

这是怎么可能的,因为我们没有将ThreadContext,userId或trasactionId作为参数传递给printThreadContextValues?

package ccs.progest.javacodesamples.threadlocal.ex1;public class PrintThreadContextValues {public static void printThreadContextValues(){System.out.println(CustomThreadLocal.get());}
}

很简单

从CustomThreadLocal的内部映射调用CustomThreadLocal.get()时,将检索与当前线程关联的对象。

现在,让我们看看何时使用真正的ThreadLocal类的示例。 (上面的CustomThreadLocal类只是为了了解ThreadLocal类背后的原理,该原理非常快并以最佳方式使用内存)

package ccs.progest.javacodesamples.threadlocal.ex2;public class ThreadContext {private String userId;private Long transactionId;private static ThreadLocal threadLocal = new ThreadLocal(){@Overrideprotected ThreadContext initialValue() {return new ThreadContext();}};public static ThreadContext get() {return threadLocal.get();}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public Long getTransactionId() {return transactionId;}public void setTransactionId(Long transactionId) {this.transactionId = transactionId;}public String toString() {return 'userId:' + userId + ',transactionId:' + transactionId;}
}

如javadoc所述:ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段。

package ccs.progest.javacodesamples.threadlocal.ex2;public class ThreadLocalMainSampleEx2 {public static void main(String[] args) {new Thread(new Runnable() {public void run() {ThreadContext threadContext = ThreadContext.get();threadContext.setTransactionId(1l);threadContext.setUserId('User 1');//here we call a method where the thread context is not passed as parameterPrintThreadContextValues.printThreadContextValues();}}).start();new Thread(new Runnable() {public void run() {ThreadContext threadContext = ThreadContext.get();threadContext.setTransactionId(2l);threadContext.setUserId('User 2');//here we call a method where the thread context is not passed as parameterPrintThreadContextValues.printThreadContextValues();}}).start();}
}

调用get时 ,新的ThreadContext实例与当前线程关联,然后将所需的值设置为ThreadContext实例。

如您所见,结果与第一组样本相同。

userId:User 1,transactionId:1
userId:User 2,transactionId:2

(这可能是相反的顺序,所以不要担心如果您首先看到“用户2”?)

package ccs.progest.javacodesamples.threadlocal.ex2;public class PrintThreadContextValues {public static void printThreadContextValues(){System.out.println(ThreadContext.get());}
}

ThreadLocal的另一种非常有用的用法是当您有一个非常昂贵的对象的非线程安全实例时的情况。我发现的大多数极性示例是使用SimpleDateFormat(但很快我将提供另一个使用Webservices端口的示例)

package ccs.progest.javacodesamples.threadlocal.ex4;import java.text.SimpleDateFormat;
import java.util.Date;public class ThreadLocalDateFormat {// SimpleDateFormat is not thread-safe, so each thread will have oneprivate static final ThreadLocal formatter = new ThreadLocal() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat('MM/dd/yyyy');}};public String formatIt(Date date) {return formatter.get().format(date);}
}

结论:

线程局部变量有很多用途,这里仅描述两种:(我认为使用最多的)

  • 真正的每线程上下文,例如用户ID或事务ID。
  • 每线程实例以提高性能。

参考: Java代码样本博客中的JCG合作伙伴 Cristian Chiovari 了解了ThreadLocal的概念 。


翻译自: https://www.javacodegeeks.com/2012/07/understanding-concept-behind.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371777.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python加密模块教程_Python加密模块的hashlib,hmac模块使用解析

这篇文章主要介绍了Python加密模块的hashlib,hmac模块使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下在写搬砖脚本中,碰到一个加密的信号标签文件无法运行。import hashlibimport timem ha…

DAO层–救援通用

泛型可以是使用编译时验证(类型安全性)的功能来创建可重用代码的强大工具。 不幸的是,我感到主流开发人员仍然对此感到恐惧。 但是,比喻海格的蜘蛛,我会说泛型是被严重误解的生物……:-) 我希望以下示例可以证明它们…

ThreadLocal详解(实现多线程同步访问变量)

ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。 这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都 在操…

SCREEN屏幕编程时候必须保证SCREN中词典的字段格式必须和数据表中字段的类型长度一致!...

此时任意操作都会出现如下问题 /h调试 回车调试被激活任意操作 执行到第23行时候报错“请输入一个数值”,检查数据表中字段参考数据元素以及对应的域均是char类型,此时检查screen屏幕设置字段类型,/n退出程序 重新进入程序 单击 点击屏幕9000…

mysql 阿里云 版本_关于阿里云centos版本,mysql5.7的一些注意事项

1.阿里云进去mysql是默认已经安装好了的,只需要修改root用户的密码。关于修改密码:1)登陆阿里云,进入root目录,会有mysql的.sh文件,可以通过运行该文件得到初始密码。此时用初始密码登陆mysql,use mysql 切…

JAXB –不需要注释

似乎存在一个误解,认为在模型上需要使用批注才能使用JAXB(JSR-222)实现。 事实是,JAXB是例外配置,因此仅当您要覆盖默认行为时才需要注释。 在此示例中,我将演示如何在不提供任何元数据的情况下使用JAXB。 …

zabbix 3.0.3 (nginx)安装过程中的问题排错记录

特殊注明:安装zabbix 2.4.8和2.4.6遇到2个问题,如下:找了很多解决办法,实在无解,只能换版本,尝试换(2.2.2正常 | 3.0.3正常)都正常,最后决定换3.0.31、Error connecting …

安装mysql5.7.24rpm_centos7安装mysql-5.7.24(rpm安装)

关于mysql的4个rpm包node[rootelk-200 ~]# ls mysql/ -lhtotal 192M-rw-r--r-- 1 root root 25M Aug 26 12:38 mysql-community-client-5.7.24-1.el7.x86_64.rpm-rw-r--r-- 1 root root 275K Aug 26 12:38 mysql-community-common-5.7.24-1.el7.x86_64.rpm-rw-r--r-- 1 root ro…

Java锁实现

我们都使用第三方库作为开发的正常部分。 通常,我们无法控制其内部。 JDK随附的库是一个典型示例。 这些库中的许多库都使用锁来管理竞争。 JDK锁具有两种实现。 人们使用原子CAS样式指令来管理索赔过程。 CAS指令往往是最昂贵的CPU指令类型,并且在x86上…

一键生成APP官网

只需要输入苹果下载地址,安卓市场下载地址,或者内测下载地址,就能一键生成APP的官网,方便在网上推广。 好推APP官网 www.hotapp.cn/app 转载于:https://www.cnblogs.com/likwo/p/6223889.html

python 字符ab+字符c 2_“ab”+”c”*2 结果是: (1.3分)_学小易找答案

【判断题】药物效应动力学简称药效学,是研究药物对机体的作用?【单选题】以下关于Python语言中“缩进”说法正确的是:‪‬‪‬‪‬‪‬‪‬‮‬‭‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪…

数据结构及算法 -- 目录

排序算法 -- 目录 啦啦啦转载于:https://www.cnblogs.com/ClassNotFoundException/p/7122848.html

Spring MVC集成测试

在Spring MVC中对控制器进行集成测试的一种方法是使用Spring提供的集成测试支持。 对于Junit4,此支持包括一个名为SpringJunit4ClassRunner的自定义Junit Runner和一个用于加载相关Spring配置的自定义批注。 样本集成测试将遵循以下原则: RunWith(Spri…

spark 快速入门 java API

Spark的核心就是RDD,对SPARK的使用入门也就是对RDD的使用,包括action和transformation 对于Java的开发者,单单看文档根本是没有办法理解每个API的作用的,所以每个SPARK的新手,最好按部就班直接学习scale, 那才是一个高…

网页设计上机考试原题_Dreamweaver上机考试题目dreamweaver试题库网页制作试题.doc...

网页设计上机考试题集注意:所有题目中涉及的素材都在考试文件夹内,其中图片在下面的pic文件夹中,音乐、flash在media文件夹。1) 在1.html中的顶部添加一个锚点链接,点击之能立即到达页面最底端。2) 将1.html中的所有链接的默认样式…

35数据结构与算法分析之---最短路径

本系列是阅读《数据结构与算法应用实践教程》第2版 主编 李文书 北京大学出版社 的读书笔记,加上自己的理解,更多的是学习的记录与反思,如有不妥,欢迎指正,非常感谢。转载于:https://www.cnblogs.com/guochaoxxl/p/712…

Quartz 2 Scheduler示例

Quartz是一个开源作业调度框架。 它可用于管理和计划应用程序中的作业。 步骤1:建立已完成的专案 创建一个Maven项目,如下所示。 (可以使用Maven或IDE插件来创建它)。 步骤2:图书馆 Quartz依赖项已添加到Maven的po…

sql server 2008 com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机

原内容搬迁到了新网站,给你带来的不便,敬请谅解! 》 http://www.suanliutudousi.com/2017/08/28/sql-server-2008-com-microsoft-sqlserver-jdbc-sqlserverexception-%E9%80%9A%E8%BF%87%E7%AB%AF%E5%8F%A3-1433-%E8%BF%9E%E6%8E%A5%E5%88%B0…

如何通过网线连接两台电脑快速传输数据?

介绍 我们经常需要拷贝文件会用到类似U盘等工具,但我们有时在传输大文件时又苦于没有,那么大内存的转存工具。这时候我们就可以通过一条小小的网线连接两台电脑,形成一个小的局域网传输数据,因为是通过网线传输,所以传…

30分钟内使用MongoDB

最近,我被NoSQL错误咬住了-或是我的同事Mark Atwell提出的“燃烧在哪里!” 运动。 尽管我无意于在不久的将来或可预见的将来回避友好的“ SELECT ... WHERE”,但我确实设法弄懂了一些代码。 在本文中,我分享了我在NoSQL世界中首次…