- 浏览: 197401 次
- 性别:
- 来自: 杭州
-
文章分类
最新评论
-
asdiy:
very good!原来是这个原因!
为什么会"well-known file is not secure" ? -
豆浆王子:
...
在Struts2的Action中取得请求参数值的几种方法 -
key232323:
skyfen 写道LZ是个很棒的人,加油!
+1
我四年的职业生涯,2010路在何方? -
skyfen:
LZ是个很棒的人,加油!
我四年的职业生涯,2010路在何方? -
y112300:
天下乌鸦一般黑,怎一个乱子了得
我四年的职业生涯,2010路在何方?
写这篇帖子的目的不是为了来剖析ThreadLocal,因为坛子里有许多高手已经深入浅出的把ThreadLocal讲解的很清楚了。
特别是lujh99的正确理解ThreadLocal这篇帖子,通过JDK源代码把ThreadLocal讲得非常深入浅出,让我深受启发。我写这篇帖子的目的只是为再此作一个补充,想以另外一种通俗易懂的表达方式把自己对ThreadLocal理解写出来。
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
正如lujh99 所言,ThreadLocal不是用来解决对象共享访问问题的,而是为了处理在多线程环境中,某个方法处理一个业务,需要递归依赖其他方法时,而要在这些方法中共享参数的问题。例如有方法a(),在该方法中调用了方法b(),而在b方法中又调用了方法c(),即a-->b--->c,如果a,b,c都需要使用用户对象,那么我们常用做法就是a(User user)-->b(User user)---c(User user)。但是如果使用ThreadLocal我们就可以用另外一种方式解决:
- 在某个接口中定义一个静态的ThreadLocal 对象,例如 public static ThreadLocal threadLocal=new ThreadLocal ();
- 然后让a,b,c方法所在的类假设是类A,类B,类C都实现1中的接口
- 在调用a时,使用A.threadLocal.set(user) 把user对象放入ThreadLocal环境
- 这样我们在方法a,方法b,方法c可以在不用传参数的前提下,在方法体中使用threadLocal.get()方法就可以得到user对象。
上面的类A,类B ,类C就可以分别对应我们做web开发时的 web层的Action--->业务逻辑层的Service-->数据访问层的DAO,当我们要在这三层中共享参数时,那么我们就可以使用ThreadLocal 了。
那么user对象是如何存放到ThreadLocal 中的?
经过我的测试,正是如此,当我们调用A.threadLocal.set(user) 时,set()做了以下几件事:
- 得到当前线程Thread 对象
- 通过当前线程对象得到当前线程的ThreadLocalMap 对象
- 将ThreadLocal静态实例作为key,user对象作值,存放到ThreadLocalMap 中。
PS:因为ThreadLocal是静态的,所以每个线程中的ThreadLocalMap的key都是相同的,不同的只是存放的容器ThreadLocalMap。
总结:
其实ThreadLocal 跟我们做web开发时使用的session对象的作用很类似,每当我们向服务器发送一个请求时,web服务器会为该请求创建一个线程,同时会为该请求创建一系列对象,其中包括session(当然在同一个浏览器发送的请求都获得是同一个session对象),所以当我们做web开发时,可以不用担心线程安全问题,自由的往session中存取变量,保存用户状态。同理,当我们在程序中第一次使用A.threadLocal.set(user) 存放参数时,不管在程序的哪个地方,我们都可以通过ThreadLocal 所在的接口访问静态threadLocal对象,同时来共享threadLocal存放的参数,threadLocal就相当于session的作用,来保存当前线程的状态。在我们开发实际开发中,可以任意往threadLocal中共享和存取自己需要的变量,只不过web中的session的生命周期在于客户端的浏览器,而threadLocal中存储的变量的生命周期只在于当前线程,当前结束,threadLocal中存放的参数也被销毁。
评论
ThreadLocal的本意是配合Thread以一种对象拷贝的方式解决多线程的互斥问题的。各自Thread维护自己的ThreadLocalMap,并以这个ThreadLocal的hashCode为key,用户对象为value。
如果lz用一个static的ThreadLocal,以这个ThreadLocal的hashcode()为key,User为value,并只在同一个线程里发挥作用的话,跟用public static Map,以Thread.currentThread().hashCode()为key,User为value,有什么区别吗?
threadlocal内部貌似是使用了Thread.currentThread().getName()来当做key,在jdk1.5中是一种效率比较低的做法,据说在6.0速度有提升,但是实际中用途也不是很广泛,只有在权限系统或者保存数据库Connection时才会用到。
额,你听谁说的??
JDK1.5的代码貌似如下:
private static synchronized int nextHashCode() { int h = nextHashCode; nextHashCode = h + HASH_INCREMENT; return h; }
这句话虽然说的没错,但千万不要利用线程销毁来清除ThreadLocal,正确的方法就像前面的doFilter例子那样,谁设置谁要负责清除。
所有服务器环境下都是用线程池缓存线程,由于线程不会(或不会立即)被销毁,ThreadLocal也不会被清除,更严重的问题是被缓存的线程的ThreadLocal不确定的会被用到其他服务请求处理中。
假如用ThreadLocal设置了user,而不及时清除,甚至会发生比较严重的安全问题。
这个问题的关键在于在web环境下怎么使用。
一般是使用Filter技术,在web.xml配置好,filter里的伪代码是这样:
doFilter(){ 设置threadlocal变量; filterChain.doFilter(); 清除threadlocal变量; }
这样就保证了在Action、JSP里都可以获得本线程的threadlocal变量。
注意:一次web 请求肯定是由一个线程来完成响应的。
这样的用法就更清析了...
不知道还有可能会出什么问题.....
不想用了新的用法会出什么隐问题
这样用就没问题了
doFilter(){ 设置threadlocal变量; try { filterChain.doFilter(); }finally { 清除threadlocal变量;} }
<div class="quote_div">
<p>[b][/b]</p>
<div class="quote_title">抛出异常的爱 写道</div>
<div class="quote_div">
<div class="quote_title">61234865 写道</div>
<div class="quote_div">我有一个地方不太明白,就是我们用SSH的时候,如果你用threadlocal来存变量,你第二次请求的时候怎么能保证获取到的线程就是和先前的线程一样的?如果不一样,那数据岂不是就丢失了?</div>
<br>同次请求的不丢就很了不起了. <br>
</div>
<p> </p>
<p> 第二次请求的时候和第一次请求的时候用的是同一个servlet实例。servlet是线程安全的对同一个servlet的请求容器只会实例会一个对像。</p>
</div>
<br>
开发是分层的,不仅jsp和servlet之间传递参数,包括service层、dao层之间传递
我不太同意这个观点,现在比如有如下的代码。这个format方法有2个线程循环的访问,每次访问完可以放回线程池中,但是因为SimpleDateFormat不是线程安全的类,所以这样访问肯定会出现并发的错误!
public class Foo { static SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss"); public static String format(Date date) { return formator.format(date); } }
那么为了避免并发的错误,可以有如下2中方案,1加synchronized,这样每次只有一个线程访问这个方法,对性能有影响
public static synchronized String format(Date date) { return formator.format(date); }
2,每次调用这个方法就生成一个新的SimpleDateFormat对象,这样会生成大量的对象,对性能不是很好
public static String format(Date date) { SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss"); return formator.format(date); }
那么如果用ThreadLocal就可以解决创建大量对象的问题和并发访问的问题,重复利用SimpleDateFormat对象
public class Foo { static ThreadLocal local = new ThreadLocal(); public static String format(Date date) { SimpleDateFormat formator = (SimpleDateFormat)local.get(); if (formator == null) { formator = new SimpleDateFormat("yyMMddHHmmss"); local.set(formator); } return formator.format(date); } }
这个我赞同

<div class="quote_title">抛出异常的爱 写道</div>
<div class="quote_div">
<div class="quote_title">61234865 写道</div>
<div class="quote_div">我有一个地方不太明白,就是我们用SSH的时候,如果你用threadlocal来存变量,你第二次请求的时候怎么能保证获取到的线程就是和先前的线程一样的?如果不一样,那数据岂不是就丢失了?</div>
<br>同次请求的不丢就很了不起了. <br>
</div>
<p> </p>
<p> 第二次请求的时候和第一次请求的时候用的是同一个servlet实例。servlet是线程安全的对同一个servlet的请求容器只会实例会一个对像。</p>
我不太同意这个观点,现在比如有如下的代码。这个format方法有2个线程循环的访问,每次访问完可以放回线程池中,但是因为SimpleDateFormat不是线程安全的类,所以这样访问肯定会出现并发的错误!
public class Foo { static SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss"); public static String format(Date date) { return formator.format(date); } }
那么为了避免并发的错误,可以有如下2中方案,1加synchronized,这样每次只有一个线程访问这个方法,对性能有影响
public static synchronized String format(Date date) { return formator.format(date); }
2,每次调用这个方法就生成一个新的SimpleDateFormat对象,这样会生成大量的对象,对性能不是很好
public static String format(Date date) { SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss"); return formator.format(date); }
那么如果用ThreadLocal就可以解决创建大量对象的问题和并发访问的问题,重复利用SimpleDateFormat对象
public class Foo { static ThreadLocal local = new ThreadLocal(); public static String format(Date date) { SimpleDateFormat formator = (SimpleDateFormat)local.get(); if (formator == null) { formator = new SimpleDateFormat("yyMMddHHmmss"); local.set(formator); } return formator.format(date); } }
很赞同你的观点。
可以解决很多参数问题.
举一个例子-CXF,很多很多。
是地,以前给项目国际化的时候用过这个。可以把rquest放在ThreadLocal里面,这样在任何方法内都可以得到当前登录用户,有时定义某个接口可能忽略了一些参数,可以这样做。。。
<p>如果在一个业务流程真的那么需要一个context存储上下文变量,那么我们应该找到一个map,把 “流程id映射map” 存储进去。</p>
<p>这样可以完全抛开Thread的概念,业务方法就不会把底层的东西都依赖进来。</p>
<p> </p>
<p>ThreadLocal还是应该应用在处理多线程的问题中。</p>
<p> </p>
<p> </p>
这个问题的关键在于在web环境下怎么使用。
一般是使用Filter技术,在web.xml配置好,filter里的伪代码是这样:
doFilter(){ 设置threadlocal变量; filterChain.doFilter(); 清除threadlocal变量; }
这样就保证了在Action、JSP里都可以获得本线程的threadlocal变量。
注意:一次web 请求肯定是由一个线程来完成响应的。
这样的用法就更清析了...
不知道还有可能会出什么问题.....
不想用了新的用法会出什么隐问题
这个问题的关键在于在web环境下怎么使用。
一般是使用Filter技术,在web.xml配置好,filter里的伪代码是这样:
doFilter(){ 设置threadlocal变量; filterChain.doFilter(); 清除threadlocal变量; }
这样就保证了在Action、JSP里都可以获得本线程的threadlocal变量。
注意:一次web 请求肯定是由一个线程来完成响应的。
<div class="quote_div">
<p>可以这么做,不过个人理解这是对ThreadLocal的误用。</p>
<p> </p>
<p>ThreadLocal充当当前线程的Context,同时也可以理解为ThreadLocal变量的作用域属于Thread。</p>
<p>业务方法的参数的生命周期可能是瞬态的,无端的放入ThreadLocal中会延长这些对象的生命周期,注意回收。</p>
<p>好的,我们会及时清理,如果真要这样做,还需要再写代码,不知有何优点。</p>
<p>同时这些业务方法没有了参数的描述会变得晦涩,还被强制的要求在同一线程的原理下工作。抽象依赖实现,不可取。</p>
<p> </p>
<p>PS:</p>
<p>“ThreadLocal不是用来解决对象共享访问问题的”------如果Java没有了多线程,大家还需要ThreadLocal么。</p>
<p>ThreadLocal 和 request的作用域完全是不同的。</p>
<p> </p>
</div>
<p> <br>其实对于ThreadLocal的这种特性,该如何使用,我觉得这个大家各有所需,各有所好,我就不在强调自己的观点。</p>
<p> </p>
<p>我也非常肯定你的观点:ThreadLocal 和request的作用域肯定是不相同的,ThreadLocal 中存放的变量的生命周期只存活于当前线程,而request的作用域在于一个请求处理结束。在tomcat的线程池环境下,基本上都是request处理完成后,线程都被回放到线程池,即request作用域结束,ThreadLocal 中ThreadLocalMap存放的变量也并没有被销毁。我说的是可以认为ThreadLocal 和 request的作用域是相同的 ,因为即使线程没被回收,我们也无非再找回之前处理过的线程,即使找回那个线程,那个线程也许被另外的request使用过,里面存放的是其他的参数,所以我在这里假设ThreadLocal 和 request的作用域是相同的是为了让大家明白,ThreadLocal 在实际使用过程中最好且也只有用于一次请求处理。</p>
<p> </p>
因为一般的servlet,JSP容器,都使用的线程池技术,所以当一个请求处理完成直到向客户端发送响应结束后,调度这个请求处理的线程没有被销毁而被放回线程池,所以threadlocal保存的变量也并没有被销毁,必须我们手动清除。
<p> </p>
<p>ThreadLocal充当当前线程的Context,同时也可以理解为ThreadLocal变量的作用域属于Thread。</p>
<p>业务方法的参数的生命周期可能是瞬态的,无端的放入ThreadLocal中会延长这些对象的生命周期,注意回收。</p>
<p>好的,我们会及时清理,如果真要这样做,还需要再写代码,不知有何优点。</p>
<p>同时这些业务方法没有了参数的描述会变得晦涩,还被强制的要求在同一线程的原理下工作。抽象依赖实现,不可取。</p>
<p> </p>
<p>PS:</p>
<p>“ThreadLocal不是用来解决对象共享访问问题的”------如果Java没有了多线程,大家还需要ThreadLocal么。</p>
<p>ThreadLocal 和 request的作用域完全是不同的。</p>
<p> </p>
发表评论
-
公司一些牛人的开放博客
2011-07-24 20:34 1089公司一些牛人的开放博客: 1 阿里中文站交互设计技术BLOG ... -
可伸缩性最佳实践:来自eBay的经验
2011-06-28 15:00 967原文转自http://www. ... -
走向架构师之路博文分享(转)
2011-02-18 11:54 1060原文转自http://cutesource.iteye.com ... -
Velocity china 2010会议的一些ppt资料
2010-12-09 15:04 1285RT,见附件 -
各种系统框架图简介
2010-10-29 11:39 2164原文出处:http://space.itpub.net/651 ... -
设计与开发Java服务器(转)
2010-08-13 16:31 803原文地址: 设计与开发应用服务器(一)------常见模式 ... -
(转贴)Ebay架构特点(HPTS 2009)
2010-07-30 14:09 1156原文转自http://www.tech-q.c ... -
代理模式的实现
2010-05-16 12:40 01.使用CGLIB 2.使用代理类
相关推荐
- **线程局部变量**:通过`ThreadLocal`类来隔离不同线程中的变量副本,从而避免线程安全问题。 ### 总结 本文从多线程的基础概念出发,深入探讨了Java中多线程的实现机制及线程安全问题,并介绍了几种常见的设计...
Java实验报告是对学习Java编程过程中进行的实践活动的记录和总结,通常包含实验目的、环境配置、实验内容、代码实现、运行结果以及实验体会等部分。这份"java实验报告(所有的完整的)"涵盖了全面的Java学习内容,...
这本"Java并发编程学习笔记"可能是作者在深入研究Java并发特性、工具和最佳实践过程中积累的心得体会。下面,我们将根据这个主题,探讨一些关键的Java并发编程知识点。 1. **线程与进程**:在多任务环境中,线程是...
内容概要:本文详细介绍了利用FLAC3D进行隧道台阶法施工模拟的方法和技术细节。首先解释了隧道台阶法施工的基本流程,重点在于开挖命令的应用,如'zone cmodel assign'和'zone remove'用于改变区域本构模型并执行开挖操作。接着阐述了支护结构的设置方法,包括超前加固体、初衬、二衬、锚杆和锁脚锚杆的具体配置方式。此外,还讲解了如何通过'mesh'命令直接在FLAC3D中生成符合实际工程需求的网格模型。最后展示了模拟后的围岩体位移云图和应力云图,验证了计算结果的有效性,强调了这些数据对优化施工方案的重要性。 适合人群:从事岩土工程、隧道工程及相关领域的工程师和技术人员。 使用场景及目标:适用于需要进行隧道施工模拟的专业人士,旨在提升他们对FLAC3D的理解和应用能力,确保隧道施工的安全性和高效性。 其他说明:文中提供的实例和命令操作均基于真实项目经验,有助于读者更好地理解和掌握FLAC3D的实际应用技巧。
内容概要:本文介绍了纤维骨料细观尺度混凝土模型的设计与应用,重点在于如何通过控制骨料尺寸和体积率,在不同有限元软件(如Abaqus、Ansys、Ls-Dyna、Flac3d)中进行有效的四面体网格划分和六面体网格投影。文中提供了生成随机骨料位置和直径的Python代码片段,并详细解释了网格划分过程中需要注意的技术细节,如碰撞检测、网格转换公式以及材料属性设置。此外,还讨论了模型验证的方法及其在实际工程项目中的应用价值。 适合人群:从事土木工程、材料科学领域的研究人员和技术人员,尤其是那些需要利用有限元方法进行混凝土结构分析的专业人士。 使用场景及目标:①帮助工程师更好地理解和预测纤维混凝土的行为特性;②为实际工程项目提供理论支持和技术指导,从而优化纤维混凝土的应用;③提高仿真精度,减少实验成本和时间。 其他说明:文中提到的一些具体操作步骤和技术细节对于初学者来说可能具有一定挑战性,建议读者在实践中逐步掌握相关技能并积累经验。同时,正确设置物理量单位非常重要,错误的单位可能导致计算结果严重偏离预期。
嵌入式八股文面试题库资料知识宝典-c++个人笔记总结.zip
内容概要:本文详细介绍了西门子S7-1200 PLC在工业自动化领域的应用,重点讲解了其模块、板卡和通讯方式。首先概述了PLC模块和板卡作为基本单元的作用,接着深入探讨了支持的多种通讯协议,包括Modbus-RTU、S7通讯、Modbus-TCP和TCP/IP等。每种协议都配有具体的代码分析和调试方法。最后,介绍了博途V16编程软件的使用体验,强调了其对S7-1200 PLC编程的支持。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对西门子S7-1200 PLC有初步了解或希望深入了解的人群。 使用场景及目标:适用于需要掌握PLC模块化设计、不同通讯协议的应用场景,旨在帮助读者理解PLC的工作原理,提高编程和调试能力,从而更好地应用于实际项目中。 其他说明:文中提供的实例和代码分析有助于读者快速上手,同时推荐使用博途V16及以上版本的编程软件进行实践操作。
内容概要:本文介绍了Comsol仿真软件在等离子体空气反应领域的应用,重点探讨了其无模型反应框架的功能。该框架能模拟超过40种气体(如氧气、氮气、氦气)的详细反应过程,提供碰撞截面数据、迁移率扩散系数、速率系数和汤森系数的查询与求解功能,并通过bosig+模块实现自定义反应路径的选择。此外,文中强调了代码分析与实践应用的重要性,以及这些功能如何提升等离子体反应研究的效率和准确性。 适合人群:从事等离子体物理、化学反应动力学及相关领域研究的专业人士和技术人员。 使用场景及目标:适用于需要精确模拟复杂等离子体环境中气体反应的研究项目,旨在提高对等离子体反应机制的理解,优化实验设计,预测反应行为。 其他说明:Comsol仿真软件凭借其强大的计算能力,在等离子体研究中扮演着重要角色。随着技术的发展,该框架有望进一步推动相关领域的创新和发展。
嵌入式八股文面试题库资料知识宝典-同方万维硬件测试工程师.zip
嵌入式八股文面试题库资料知识宝典-c,c++笔试.zip
少儿编程scratch项目源代码文件案例素材-激光连接.zip
嵌入式八股文面试题库资料知识宝典-奔图电子-软件笔试试题v1.1(C,C++工程师).zip
嵌入式八股文面试题库资料知识宝典-国科环宇有限公司.zip
基于LDA主题模型对AIGC的影响力分析.pdf
可以自己添加应用和功能版,在/opt/upt/apps/下面添加ubin目录和ulib目录,把你想用的程序添加到ubin,支持模块添加到ulib中,就可以运行,具体刷机操作,请看《》
内容概要:本文探讨了遗传算法在车辆路径优化问题(VRP)中的应用及其改进,特别是在冷链物流、软时间窗和多配送中心场景下的路径优化策略。文中介绍了遗传算法通过模拟自然界进化过程来寻找最优路径解决方案的能力,并详细讨论了其在冷链物流中的重要性,即确保产品运输过程中的温度稳定和时效性。此外,还提到了软时间窗概念的应用,以平衡客户满意度和运输成本。在多配送中心场景下,遗传算法能有效处理复杂路径规划问题,如外卖配送路径优化和充电桩电车车辆路径优化。除了遗传算法,蚁群算法、模拟退火算法和粒子群算法也在不同类型的路径优化问题上得到广泛应用,如旅行商问题(TSP)、容量约束的车辆路径规划(CVRP)和带距离、容量和时间窗约束的车辆路径规划(VRPTW)。最后,文章强调了遗传算法改进的研究方向,旨在提高运算速度和精度,从而提升物流效率和客户满意度。 适合人群:从事物流与运输领域的研究人员和技术人员,对车辆路径优化感兴趣的学者和从业者。 使用场景及目标:适用于冷链物流、外卖配送、充电桩电车等多种实际应用场景,旨在优化路径规划,降低运输成本,提高客户满意度。 其他说明:本文不仅介绍了现有算法的应用情况,还指出了未来可能的研究方向和发展趋势。
内容概要:本文详细介绍了物流领域的车辆路径优化(VRP)及其扩展问题——带时间窗的车辆路径优化(VRPTW),并探讨了冷链物流车辆路径优化(考虑充电桩需求)。文中通过MATLAB实现了遗传算法解决这些问题的具体步骤,包括参数设置、种群初始化、适应度函数计算、遗传算法循环等。此外,还讨论了多配送中心场景下的路径优化挑战和其他优化算法(如蚁群算法、粒子群算法、节约算法和模拟退火算法)的应用。最后,针对冷链物流和电动汽车路径优化提出了具体的解决方案和技术细节。 适合人群:从事物流管理、运筹学、算法设计的研究人员和工程师,尤其是对MATLAB有一定基础的技术人员。 使用场景及目标:适用于需要优化物流配送路径的企业和个人,旨在提高配送效率、降低成本、提升服务质量。具体应用场景包括但不限于城市配送、冷链运输、电动车辆调度等。 其他说明:文中提供了完整的MATLAB代码示例,帮助读者更好地理解和实践各种优化算法。同时,强调了不同算法的特点和适用条件,便于读者根据实际情况选择最合适的算法。
嵌入式八股文面试题库资料知识宝典-文思创新面试题2.zip
嵌入式八股文面试题库资料知识宝典-网络编程.zip
少儿编程scratch项目源代码文件案例素材-火柴人防御.zip