彻底理解ThreadLocal
来源:互联网 发布:淘宝卖衣服代理兼职 编辑:程序博客网 时间:2024/06/12 23:23
彻底理解ThreadLocal
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
ThreadLocal是什么
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
- void set(Object value)设置当前线程的线程局部变量的值。
- public Object get()该方法返回当前线程所对应的线程局部变量。
- public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:
通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个TestNum实例。运行以上代码,在控制台上输出以下的结果:
thread[Thread-0] --> sn[1]thread[Thread-1] --> sn[1]
thread[Thread-2] --> sn[1]
thread[Thread-1] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-1] --> sn[3]
thread[Thread-2] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-2] --> sn[3]
考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个TestNum实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
Thread同步机制的比较
ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
Spring使用ThreadLocal解决线程安全问题我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:
同一线程贯通三层这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单3 TestDao:非线程安全
由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:
代码清单4 TestDao:线程安全
不同的线程在使用TopicDao时,先判断connThreadLocal.get()是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。
当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一事务多DAO共享同一Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。
ConnectionManager.java
java.lang.ThreadLocal<T>的具体实现
那么到底ThreadLocal类是如何实现这种“为每个线程提供不同的变量拷贝”的呢?先来看一下ThreadLocal的set()方法的源码是如何实现的:
在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。
线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。
为了加深理解,我们接着看上面代码中出现的getMap和createMap方法的实现:
接下来再看一下ThreadLocal类中的get()方法:
再来看setInitialValue()方法:
获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。
进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。
小结
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
ConnectionManager.java
后记
看到网友评论的很激烈,甚至关于ThreadLocalMap不是ThreadLocal里面的,而是Thread里面的这种评论都出现了,于是有了这个后记,下面先把jdk源码贴上,源码最有说服力了。
源码就是以上,这源码自然是在ThreadLocal里面的,有截图为证。
本文是自己在学习ThreadLocal的时候,一时兴起,深入看了源码,思考了此类的作用、使用范围,进而联想到对传统的synchronize共享变量线程安全的问题进行比较,而总结的博文,总结一句话就是一个是锁机制进行时间换空间,一个是存储拷贝进行空间换时间。
(全文完)
- 顶
- 153
- 踩
- 28
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- 彻底理解ThreadLocal
- kindeditor在bootstap模态框modal中点击全屏空白无效,高度宽度拖动调整
- Linux多台服务器间SSH免密码登录配置
- Mybatis入门
- spring boot zuul路由
- 王汎森:如果让我重做一次研究生
- 彻底理解ThreadLocal
- Java学习笔记之集合(四):LinkedList集合的特点和常用方法
- CSDN中上传图片的问题
- java集合类多条件排序
- 人脸检测发展:从VJ到深度学习(下)
- 中國居民至台灣地區旅遊前置作業與相關限制(自由行、不論特殊情況)
- 【ExtJs学习笔记】6.ExtJs布局
- hdu 6215 链表
- Android 安装apk
推荐一篇文章
很经典
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
<font color=red>ThreadLocal值附属于这个线程,map的通过ThreadLocal类来进行维护</font>
至于getMap确实是从Thread那里拿的引用来用的,但是如果拿到的map为空的时候,这个时候就要调用createMap(Thread t, T firstValue)方法,这个方法里面就是初始化当前线程里面的ThreadLocalMap,具体操作是
t.threadLocals = new ThreadLocalMap(this, firstValue);
这个方法是在ThreadLocal中的。也就是说第一个ThreadLocal变量在设置值的时候初始化了当前线程的ThreadLocalMap,后面的ThreadLocal变量,直接用初始化好的Map来存储。
当然还有其他的方法初始化ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
这里有个getMap(t)方法是获取ThreadLocalMap ,那我们来看看这个getMap(t)是从哪里拿到的:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这里的t从get() 方法的第一行可以是当前线程,也就是说,
ThreadLocalMap map = getMap(t);这一行代码是在从当前线程中获取ThreadLocalMap 的实例对象,所以说ThreadLocalMap这个类虽然是定义在 ThreadLocal中,是ThreadLocal的内部类,但是ThreadLocal自身并没有ThreadLocalMap 的实例对象,ThreadLocal他用到的ThreadLocalMap对象是从当前线程中获取的,获取的是当前现场Thread的一个成员变量
/*
* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal本身使用Map也是通过当前线程去获取这个map的...它自身没有维护map的引用(更多的是维护了一个自身对应的哈希码)...
所以评论中说的ThreadLocalMap不是Local中的.也是有道理的.只是不严谨.
推荐一个:
http://songuooo.com/2014/8/10/threadlocal-why-0x61c88647
通过查看源码可知,在调用set 与 get 的时候,都会根据当前线程,返回当前线程的ThreadLocalMap对象,如果没有,则new一个,以便之后使用。
public class Thread implements Runnable {
xxxxxxxx;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
xxxxx
}
get 与set 的时候,都是获取的thread里面的,每个线程维护一份
android 的是数组
而且ThreadLocalMap的键是ThreadLocal而不是Thread!您仔细看一下好吗?
我研究这个threadlocal时,百度一搜,第一个看的就是你这个,真是坑啊,希望您重新看一下这个知识点,修正自己的文章!
正确的解释就在源码中,和这篇文章:http://www.cnblogs.com/dolphin0520/p/3920407.html
还有一个很有利的证据就是ThreadLocalMap 的KEY用的是不同的线程,因为每次调用的方法肯定能产生不能的线程,所以这就不难解释为什么每次调用的方法可以取到自己对应的类属性的值。
至于ThreadLocal与多线程的关系,我开始讲了,每个方法的调用就会产生一个新的线程,所以每个线程想要拿到自己对应的公共变量的值,肯定不能用类似private String abc; 这样的类公共属性,你想呀,类是单例的,势必谁都会修改abc里面的值,但又没有其它的解决办法,因为before跟 after是处于两个不同的方法,用局部变量,两个方法之间没法传递,用公共变量又会被其它线程所修改,所以ThreadLocal作用就在于,即是公共变量,又只能被自己的线程所修改调用,如此这般,比如在性能方面,before方法里面记录一个开始时间放在ThreadLocal里面,然后再after方法里面获取结束之间,两个时间之差即是所消耗时间,perfect,希望我的理解对大家有所帮助,不妥之处多多欢迎沟通交流,3Q
@Component
public class AnalysisAop {
private ThreadLocal<Long> startTime = new ThreadLocal<Long>();
private ThreadLocal<String> id = new ThreadLocal<String>();
private ThreadLocal<String> name = new ThreadLocal<String>();
@Before
public void beforeExec(JoinPoint joinPoint){
@After
public void afterExec(JoinPoint joinPoint){
public static void closeConnect(){
connectionHolder.get().close(); // 关闭 conn
connectionHolder.remove(); // 移除 ThreadLocal
}
说的真好
----------------------------------------------------
一二两句话在定义上就错了,两个解决的根本不是一个问题,而不是像楼主这句话表达的都是解决相同变量的访问冲突,就像是比较一个人吃饭和上学一样,虽然涉及的主体都是人(线程),但一个是为了更好地吃饭,一个是为了更好地上学,根本没有可比性。
废话说了这么多,例子也不恰当,其实就一句话,两个东西根本不是一类东西,没有可比性,拿来比较本身就是错误
ThreadLocal 的工作原理在于:每个Thread调用到ThreadLocal 的set ( T )方法时,其实内部实现的就是ThreadLocal 对象创建一个ThreadLocalMap对象塞到调用的那个Thread局部对象中。真正保存的数值都存在每个Thread自己那边,当然线程之间没什么影响啊。
说明下ThreadLocalMap 中保存的值:类似于 Map(key,value) 保存的,key=ThreadLocal 对象,value=你传递的值对象。 这个ThreadLocalMap 保存在每个调用的Thread那边!
这句话有点含糊,有的可能以为用ThreadLocal 就完全能代替锁机制了。锁机制是内存共享问题,而ThreadLocal是解决代码共享问题。 其实我很崇拜博主,么么哒
这句对ThreadLocal实现的理解完全错误,希望网友不要被误导,实际上ThreadLocal类中并没有Map,而是在每个Thread中有各自对应的ThreadLocalMap,每个ThreadLocalMap都是以ThreadLocal实例为键存储变量值的,需要存储多个变量值的话就需要多个ThreadLocal实例。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
看到上面这句话就直奔评论来了
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
比较深入详细地介绍了ThreadLocal的实现原理,并讨论了ThreadLocal到底会不会引发内存泄露。
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
比较详细深入地介绍了ThreadLocal的实现原理,并探讨了ThreadLocal是否真的会引起内存泄露。
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
比较详细深入地介绍了ThreadLocal的实现原理,并探讨了ThreadLocal是否真的会引起内存泄露。
楼主说的“以空间换时间”和你说的"每一个线程会隔离开来存储数据"是一个意思。
在我们大部分的使用都是将ThreadLocal作为一个静态的变量来使用的。因此,楼主这句话很容易让人误解啊,,,切记,不要偷懒,最好用完记得要remove!!!否则,怎么死都不知道!
5楼说的很对,threadlocal压根就不是为了解决变量共享的问题的,只是为了避免同一线程中对象参数传递而提出的。
至于这位兄弟,我想说“同步个卵啊,你自己都翻译出来了‘这个类提供线程内部变量,这些变量和正常变量不同,因为每个线程都有自己并独立初始化的变量拷贝。ThreadLocal实例通常是私有静态变量,并通常和线程的状态有关。’”
每个线程中都有一个独立的threadlocal变量拷贝,你告诉我咋跨线程同步?能不能看看源码啊。。。少年
为博主的分享精神点赞,但是希望博主以后能尽量理解准确之后再发文,不然容易将新手带歪的....如果理解之后好歹把博文改一下哈
1、我有一个类,在实例化的时候需要消耗大量的系统资源的和时间,但是在单例模式下又会存在线程安全的问题;
2、尽管在单例模式下会有线程安全的问题,我依然使用单例实现,只是将geInstance方法略作修改,在“geInstance”方法中加入ThreadLocal,每次都从ThreadLocal中get我的对象实例;
3、这样既不用重复繁重的创建对象实例,然后因为ThreadLocal的线程隔离,也不用考虑线程安全问题;
以上是我的理解,不知道是否正确,或存在不足之处,希望大家赐教!
1.ThreadLocal不是用来解决共享对象的多线程访问问题的,一定要扯上关系,结果就是这样,大家都看不懂你在说什么。
2.ThreadLocal的实现并不是楼主说的在其内部提供了一个Map,用Thread作为key;用Thread作为key就会存在共享变量的同步与并发问题,会对ThreadLocal进行加锁,楼主源码已经很清晰的说明了实现机制,我就不分析了。
3.ThreadLocal并不是为线程保存了对象的副本,ThreadLocal是为每个线程隔离了一个类的实例,这个实例的作用于仅限于线程内部。楼主的“序列号”的例子不够贴切,因为对原始类型的赋值操作都是对这个值的拷贝。楼主可能要说,那我这是Integer类型,怎么说?那我要说的是,原始类型的封装类型在做运算的时候,先解封,再运算,然后把结果重新封装成新的对象,显然,这依然是一个副本,所以,不能说是为线程保存了对象的副本,楼主选择例子一定要慎重。。
4.如果真的涉及到多线程共享的时候,ThreadLocal是解决不了这个问题的,既然作为共享变量,ThreadLocal并不是为线程提供共享变量的副本,而仅仅是为线程隔离对象,那我要问了,为线程隔离共享变量的意义是什么?毫无意义,本来就是共享的变量,隔离的目的是让其他线程无法访问,这已经违背了共享变量的目的了。。对共享变量的读写是需要同步机制实现的。。。
后面我来简单解释一下ThreadLocal的作用~
我们从这个类的名字就能了解到类的作用,ThreadLocal可以分解为Thread,和Local,前者就不多说了,后者的意思是局部,整个类名可以理解为:线程局部对象。程序是运行在线程中的,所以,在整个运行过程中,在任何地方都可以获得这个线程的局部对象,你从线程内部取出的局部对象难道会变成其他线程的对象么?显然不会啊。
大概意思翻译一下:
这个类提供线程内部变量,这些变量和正常变量不同,因为每个线程都有自己并独立初始化的变量拷贝。ThreadLocal实例通常是私有静态变量,并通常和线程的状态有关。
据我所知Servlet是单例的,但是是支持多线程的,也就是说多个线程会使用同一个Servlet。虽然这样,线程A使用该Servlet的doservice()是在线程A中,线程B使用该Servlet的doservice()是在线程B中,那么doservice()中的DAO层必然也在同一个线程中。
小弟菜鸟,求LZ耐心解释,谢谢。
假设有请求RA和请求RB,他们都会去访问ServletA(,简称SA),那么这时肯定是在两个线程(假设线程A,B)中同时跑SA。
在SA中有dopost方法,该方法执行某个Service的doservice()方法;
doservice(){
DAO d1=new DAOA();
d1.dodaoa();
DAO d2=new DAOB();
d2.dodaob();
}
那么这样下来的必定每个请求对应一个线程,所以各自的doservice()方法在各自的线程中执行,所以仔线程A中当DAOA会先新建Connection,DAOB也用DAOA新建的Connection,同样在线程B中也是这么个情况。
所以我觉得这句话下面不对,不知道是我哪一步理解错了,号纠结啊。
“当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一事务多DAO共享同一Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。“
if (connThreadLocal.get() == null) {
Connection conn = getConnection();
connThreadLocal.set(conn);
return conn;
}
不知道你的getConnection(); 是怎么获得的,如果一个线程中先DAOA,发现该线程没有Connection便从连接池中拿一个并保存在当前线程中,接着同一个线程中的DAOB执行,发现有当前线程已经有Connection,就直接用了,还是同一个啊。
不知道是你和我谈的不是一样的东西,还是我真的想错了?
connThreadLocal.set(conn); 这里set之后放在threadLocal里面的好么?如果有不止AB,还有CDEFG等同时并发访问,看到的都是Null那么他们都会自己去getConnection,这样就会产生多个了!!!
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
protected Connection initialValue() {
Connection conn = null;
try {
conn = DriverManager.getConnection(
url,username,password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
public static void setConnection(Connection conn) {
connectionHolder.set(conn);
}
}
public class TestService {
public void doservice() throws SQLException{
Statement stat1 = ConnectionManager.getConnection().createStatement();
stat1.execute("sql1");
Statement stat2 = ConnectionManager.getConnection().createStatement();
stat2.execute("sql2");
}
}
我的意思就是一个线程访问doservice() 方法,里面两次获取(ConnectionManager.getConnection())的connection必然是同一个connection,而不同线程必然获得不同的connection,难道不是这样吗?
// ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
// 并将其保存到线程本地变量中。
if (connThreadLocal.get() == null) {
Connection conn = getConnection();
connThreadLocal.set(conn);
return conn;
} else {
return connThreadLocal.get();// ③直接返回线程本地变量
}
}
这个是改进之前的,这里就存在线程安全的问题
public void doservice() {
Statement stat1 = ConnectionManager.getConnection().createStatement();
stat1.execute("sql1");
Statement stat2 = ConnectionManager.getConnection().createStatement();
stat2.execute("sql2");
}
如果从Action到Service都是一个线程,那么在某个service中有
doservice(){
DAO d1=new DAOA();
d1.dodaoa();
DAO d2=new DAOB();
d2.dodaob();
}
那此时难道他们不再同一个线程中,获取的是不同的Connection,我不太明白,求LZ解释?
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
在DAO 还是在DAOA/DAOB里。