介绍

我知道本地线程,但直到最近才真正使用过它。
因此,我开始深入研究该主题,因为我需要一种传播某些用户信息的简便方法
通过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

了解ThreadLocal背后的概念相关推荐

  1. threadlocal_了解ThreadLocal背后的概念

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

  2. 这四行棘手的C代码背后的概念

    本文翻译自:Concept behind these four lines of tricky C code Why does this code give the output C++Sucks ? ...

  3. java pojo使用_在POJO中使用ThreadLocal进行Java嵌套事务

    java pojo使用 大多数嵌套事务是使用EJB实现的,现在我们尝试在POJO上实现嵌套事务. 在这里,我们使用了ThreadLocal的功能. 了解嵌套事务 事务可以嵌套在另一个内部. 因此,内部 ...

  4. 在POJO中使用ThreadLocal的Java嵌套事务

    大多数嵌套事务是使用EJB实现的,现在我们尝试在POJO上实现嵌套事务. 在这里,我们使用了ThreadLocal的功能. 了解嵌套事务 事务可以嵌套在另一个内部. 因此,内部事务或外部事务可以回滚或 ...

  5. python变量标识符_简谈-Python的注释、变量类型、标识符及关键字

    在Python程序中,要想支持中文输出,则要在代码前面添加 标识符:开发人员在程序中自定义的一些符号和名称 标示符是自己定义的,如变量名 .函数名等 标识符的规则: 标示符由字目.下划线和数字组成,且 ...

  6. Nginx 教程:基本概念

    戳上面的蓝字关注我们哦! 精彩内容 精选java等全套视频教程 精选java电子图书 大数据视频教程精选 java项目练习精选 英文:netguru,翻译:开源中国 www.oschina.net/t ...

  7. 以眼睛的名义:一些光度学概念的解析

    以眼睛的名义:一些光度学概念的解析 流明(lm).勒克斯(lux).坎德拉(cd).坎德拉每平方米(cd/m2).这些单位我们在日常生活中多多少少都有听过,对于和光打交道的同学更是不会陌生.本文就将对 ...

  8. 音乐雷达 shazam算法_shazam等音乐搜索应用程序的工作背后,创建您自己的音乐搜索应用程序...

    音乐雷达 shazam算法 Ever wondered how your favorite music searching app works?. Well, this is the correct ...

  9. 手把手教你理解决策树:从概念到应用

    全文2.5K字,建议阅读时间5分钟. 尽管决策树在机器学习中的使用已经存在了一段时间,但该技术仍然强大且受欢迎.本指南首先提供对该方法的介绍性知识,然后向您展示如何构建决策树,计算重要的分析参数以及绘 ...

最新文章

  1. 【C++】【五】循环链表
  2. 关于ftp的说法错误的是_斯坦福教授:成长型思维的养成,只需要换个说法,思维是可以训练的!...
  3. 在 Gitee 上使用 GPG公钥(Beta版)
  4. 【转】DevOps到底是什么意思?
  5. 数值优化(Numerical Optimization)学习系列-目录
  6. mysql xp系统时间_【Mysql5.5 XP系统下载】mysql XP系统安装图解
  7. TP框架控制器的空操作
  8. Bing搜索背景图抓取
  9. SHELL下获得指定进程的进程号,并截取为整数
  10. SQK Server提示:安装程序无法与下载服务器联系。请提供 Microsoft R Open 和 Microsoft R Server
  11. idea设置字体大小样式和背景色
  12. 【技术】如何通过局域网连接到惠普HP打印机
  13. 【编程马拉松】【014-红与黑】
  14. Frank-Cucumber - Core Frank Steps
  15. 锐捷(三)清除交换机的虚拟化(VSU)配置
  16. SIM7600CE模块UART设计指南
  17. 快速搭建小程序,实现线上引流获客
  18. 计算机的云是什么意思_什么是云计算云计算是什么意思
  19. 【EoSL】Introduction
  20. form表单注意点合集(文本域、单选框、复选框、下拉框等)

热门文章

  1. mybatis_user_guide(4) Mapper XML 文件
  2. tomcat(15)Digester库
  3. 对于高并发的理解及实践方案
  4. Angular项目打包到nginx部署过程
  5. posman mocks_使用Mocks进行需求驱动的软件开发
  6. selenium查找文本_在Selenium中查找具有链接文本和部分链接文本的元素
  7. 本地缓存防止缓存击穿_防止缓存爆炸的快速提示
  8. redis安装_Redis安装
  9. 如何在同一台计算机上安装多个Java版本
  10. lambda 序列化_Lambda,会序列化吗?