《之漫谈使用ThreadLocal改进你的层次的划分75203.docx》由会员分享,可在线阅读,更多相关《之漫谈使用ThreadLocal改进你的层次的划分75203.docx(49页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、一、什么是ThreadLocal早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。当使用ThhreaadLoocall维护变变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己
2、的副本,而不会影响其它线程所对应的副本。从线程的角角度看,目目标变量量就象是是线程的的本地变变量,这这也是类类名中“LLocaal”所所要表达达的意思思。线程局部变变量并不不是Jaava的的新发明明,很多多语言(如如IBMM IBBM XXL FFORTTRANN)在语语法层面面就提供供线程局局部变量量。在JJavaa中没有有提供在在语言级级支持,而而是变相相地通过过ThrreaddLoccal的的类提供供支持。所以,在JJavaa中编写写线程局局部变量量的代码码相对来来说要笨笨拙一些些,因此此造成线线程局部部变量没没有在JJavaa开发者者中得到到很好的的普及。ThreaadLoocall的
3、接口口方法ThreaadLoocall类接口口很简单单,只有有4个方法法,我们们先来了了解一下下:vooid sett(Obbjecct vvaluue)设置当前线线程的线线程局部部变量的的值。puubliic OObjeect gett()该方法返回回当前线线程所对对应的线线程局部部变量。puubliic vvoidd reemovve()将当前线程程局部变变量的值值删除,目目的是为为了减少少内存的的占用,该该方法是是JDKK 5.0新增增的方法法。需要要指出的的是,当当线程结结束后,对对应该线线程的局局部变量量将自动动被垃圾圾回收,所所以显式式调用该该方法清清除线程程的局部部变量并并不是必
4、必须的操操作,但但它可以以加快内内存回收收的速度度。prroteecteed OObjeectiinittiallVallue()返回该线程程局部变变量的初初始值,该该方法是是一个pprottectted的的方法,显显然是为为了让子子类覆盖盖而设计计的。这这个方法法是一个个延迟调调用方法法,在线线程第11次调用用gett()或或sett(Obbjecct)时时才执行行,并且且仅执行行1次。ThhreaadLoocall中的缺缺省实现现直接返返回一个个nulll。值得一提的的是,在在JDKK5.00中,ThhreaadLoocall已经支支持泛型型,该类类的类名名已经变变为ThhreaadLoo
5、call。APII方法也也相应进进行了调调整,新新版本的的APII方法分分别是vvoidd seet(TT vaaluee)、T gget()以及及T iinittiallVallue()。一、来看一一个实际际案例2.1同同一Seerviice方方法中调调用多个个Daoo方法可以看到,我我们有一一个Seerviice方方法,在在该Seerviice方方法中调调用多个个Daoo方法,所所有在该该Serrvicce方法法中的的的Daoo都处于于同一事事务中。该Servvicee方法结结束后,提提交事务务;该Servvicee方法中中有任何何错,回回滚事务务;2.2传传统的做做法来看下面这这段伪代代
6、码Serviice层层代码:publiic vvoidd seerviiceMMethhod()Conneectiion connn=nnulll;tryConneectiion connn=ggetCConnnecttionn();conn.settAuttoCoommiit(ffalsse);Dao1 daoo1=nnew Daoo1(cconnn);dao1.doSSomeethiing();Dao2 daoo2=nnew Daoo2(cconnn);dao2.doSSomeethiing();Dao3 daoo3=nnew Daoo3(cconnn);dao3.doSSomeethii
7、ng(); mmitt();catcch(EExceeptiion e) ttry cconnn.roollbbackk();catcch(EExceeptiion ex)finaallyytryconn.settAuttoCoommiit(ttruee);catcch(EExceeptiion e) ttry iif(cconnn!=nnulll) cconnn.cllosee(); cconnn=nuull;catcch(EExceeptiion e)每个Daoo层的代代码:Classs Daao1privaate Connnecctioon cconnn=nuull;publiic DD
8、ao11(Coonneectiion connn) tthiss.coonn=connn;publiic vvoidd dooSommethhingg() PPreppareedSttateemennt ppstmmt=nnulll; ttry psttmt=connn.ppreppareedSttateemennt(ssql); psttmt.exeecutte catcch(EExceeptiion e) llog.errror(e,”EExeccepttionn occcurrredd inn Daao1.doSSomeethiing():”+e.ggetMMesssagee,e);fi
9、naallyy ttry if(psttmt!=nuull) psstmtt.cllosee(); psstmtt=nuull; cattch(Exccepttionn e)如果我一个个Serrvicce方法法有调用用一堆ddao方方法,先先不说这这样写首首先破坏坏了OOOP的封封装性原原则,如如果有一一个daao多关关了一个个connn,那那就会导导致其它它的daao得到到的coonn为为nulll,这这种事在在这样的的写法下下由其当当你还有有业务逻逻辑混合合在一起起时很容容易发生生。笔者曾经遇遇见过22个项目目,出现现outt off meemorry或者者是coonneectiion p
10、oool hhas beeen lleakkagee,经查查代码就就是在每每个daao中多多关或者者在seerviice层层中漏关关,或者者是每个个daoo有自己己的coonnttionnconnn=ggetCConnnecttionn(),然然后还跑跑到Seerviice层层里去关关这个cconnnecttionn(那关关什么,关关个P关!)。当然,如果果你说你你在写法法上绝对对proomisse绝对对注意这这样的问问题不会会发生,但但是我们们来看看看下面的的这种做做法,是是否会比比上面这这个写法法更好呢呢?2.3 Sprringg中的做做法先来看Spprinng中的的写法。大家应该都都很熟
11、悉悉Sprringg中的写写法了,来来看一下下它是怎怎么解决决的。Serviice层层publiic vvoidd seerviiceMMethhod()try /aoop 自自动加入入connnecctioon,并并且将cconnn.seetAuutoCCommmit(fallse);dao1.doSSomeethiing();dao2.doSSomeethiing();dao3.doSSomeethiing();catcch(EExceeptiion e) /aoop 自自动加入入rolllbaackfinaallyy /aoop自动动加入cconnn.seetAuutoCCommmit(
12、truue) /aoop 自自动加入入connn.cclosse();这边我们不不讲AOOP,因因为用类类反射结结合xmml很容容易将aaop 自动。这这些东西西加入我我们的代代码中去去是不是是?我们们只管写写daoo方法,sservvicee方法,不不需要关关心在哪哪边coommiit哪边边rolllbaack何何时coonneectiion,sprringg的声明明式事务务会帮我我们负责责,这种种风格我我们称为为“优雅”,各层层间耦合合度极大大程度上上的降低低,封装装性好。因此,我们们可以总总结出下下面这些些好处: Seerviice层层的方法法只管开开启事务务(如果果讲究点点的还会会设一
13、个个Traansaactiion); 在该该Serrvicce层中中的所有有daoo使用该该serrvicce方法法中开启启的事务务(即cconnnecttionn); Daao中每每次只管管gettCurrrenntCoonneectiion(获获取当前前的coonneectiion),与与进行数数据处理理 Daao层中中如果发发生错误误就抛回回Serrvicce层 Seerviice层层中接到到exccepttionn,在caatchh中中rolllbaack,在在tryy未未尾coommiit,在在finnallly块中中关闭整整个coonneectiion。这。就就是我们们所说的的Th
14、rreaddLoccal。举个更实际际的例子子再次来来说明TThreeadLLocaal:我们有3个个用户访访问同一一个seerviice方方法,该该serrvicce方法法内有33个daoo方法为为一个完完整事务务,那么么整个wweb容容器内只只因该有有3个connnecctioon,并并且每个个connnecctioon之间间的状态态,彼此此“隔离”。我们下面一一起来看看我们如如何用代代码实现现类似于于Sprringg的这种种做法。首先,根据据我们的的ThrreaddLoccal的的概念,我我们先声声明一个个ConnnecctioonMaanagger的的类。2.4利利用ThhreaadL
15、oocall制作CoonneectiionMManaagerrpubliic cclasss CConnnecttionnMannageer prrivaate staaticc ThhreaadLoocall tll = neww ThhreaadLoocall(); prrivaate staaticc Coonneectiion connn = nuull; puubliic sstattic voiid BBegiinTrranss(boooleean begginTTranns) thrrowss Exxcepptioon iif (tl.gett() = nulll | (Coon
16、neectiion) tll.geet().iisClloseed() cconnn = SinngleetonnDBCConnnecttionn.geetInnstaancee().gettConnnecctioon(); cconnn = neww CoonneectiionSSpy(connn); iif (begginTTranns) cconnn.seetAuutoCCommmit(fallse); ttl.sset(connn); puubliic sstattic Connnecctioon ggetCConnnecttionn() thrrowss Exxcepptioon r
17、retuurn (Coonneectiion) tll.geet(); puubliic sstattic voiid cclosse() thhrowws SSQLEExceeptiion ttry (Coonneectiion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) (Coonneectiion) tll.geet().cclosse(); ttl.sset(nulll); puubliic sstattic voiid ccommmit() tthroows SQLLExccepttionn ttry
18、 (Coonneectiion) tll.geet().ccommmit(); caatchh (EExceeptiion e) ttry (Coonneectiion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) puubliic sstattic voiid rrolllbacck() thhrowws SSQLEExceeptiion ttry (Coonneectiion) tll.geet().rrolllbacck(); caatchh (EExceeptiion e) ttry (Coonneect
19、iion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) 2.5利利用ThhreaadLoocall改造Seerviice与与Daoo层Serviice层层(注意意红色标标粗-好粗yeeah,的地方方)packaage skyy.orrg.sservvicee.immpl;publiic cclasss SStuddenttSerrvicceImmpl impplemmentts SStuddenttSerrvicce puubliic vvoidd adddSttudeent(Stuudennt sstd) th
20、hrowws EExceeptiion StuudenntDAAO sstuddenttDAOO = neww SttudeentDDAOIImpll(); CClasssRooomDDAO claassRRoommDAOO = neww CllasssRooomDAAOImmpl(); ttry CConnnecttionnMannageer.BBegiinTrranss(trrue);stuudenntDAAO.aaddSStuddentt(sttd);claassRRoommDAOO.adddSttudeentCClasssRooom(stdd.geetCllasssRooomIdd()
21、, sttd.ggetssNo();ConnnecctioonMmmitt(); caatchh (EExceeptiion e) tryy CConnnecttionnMannageer.rrolllbacck(); caatchh (EExceeptiion de) tthroow nnew Exccepttionn(e); finnallly ttry CConnnecttionnMannageer.cclosse(); caatchh (EExceeptiion e) Look,如如果我把把上述标标粗(没没有加红红色)的的地方,全全部用AAOP的的方式从从这块代代码的外外部“切”进去。
22、是是不是一一个Spprinng里的的Serrvicce方法法就诞生生了?下面来看一一个完整整的例子子2.6使使用ThhreaadLoocall分离Seerviice、DAOO层先来看表结结构:T_Stuudennt表T_ClaassRRoomm表T_Stuudennt_CClasssRooom表表需求:很简单,TT_CllasssRooom表里里已经有有值了,在在插入TT_Sttudeent表表的数据据时同时时要给这这个学生生分配一一个班级级并且插插入T_Stuudennt_CClasssRooom表表,这就就是一个个事务,这这两步中中有任何何一步出出错,事事务必须须回滚。看来工程的的结构吧吧
23、:下面开始放放出所有有源代码码:2.6.11 CoonneectiionMManaagerr类packaage skyy.orrg.uutill.dbb;imporrt jjavaa.sqql.*;publiic cclasss CConnnecttionnMannageer prrivaate staaticc ThhreaadLoocall tll = neww ThhreaadLoocall(); prrivaate staaticc Coonneectiion connn = nuull; puubliic sstattic voiid BBegiinTrranss(boooleean
24、 begginTTranns) thrrowss Exxcepptioon iif (tl.gett() = nulll | (Coonneectiion) tll.geet().iisClloseed() cconnn = DBCConnnecttionn.geetInnstaancee().gettConnnecctioon(); cconnn = neww CoonneectiionSSpy(connn); iif (begginTTranns) cconnn.seetAuutoCCommmit(fallse); ttl.sset(connn); puubliic sstattic Co
25、nnnecctioon ggetCConnnecttionn() thrrowss Exxcepptioon reeturrn (Connnecctioon) tl.gett(); puubliic sstattic voiid cclosse() thhrowws SSQLEExceeptiion ttry (Coonneectiion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) (Coonneectiion) tll.geet().cclosse(); ttl.sset(nulll); puubliic s
26、stattic voiid ccommmit() tthroows SQLLExccepttionn ttry (Coonneectiion) tll.geet().ccommmit(); caatchh (EExceeptiion e) ttry (Coonneectiion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) puubliic sstattic voiid rrolllbacck() thhrowws SSQLEExceeptiion ttry (Coonneectiion) tll.geet().
27、rrolllbacck(); caatchh (EExceeptiion e) ttry (Coonneectiion) tll.geet().ssetAAutooCommmitt(trrue); caatchh (EExceeptiion e) 2.6.22 DBBConnnecctioon类packaage skyy.orrg.uutill.dbb;publiic cclasss DDBCoonneectiion prrivaate staaticc DBBConnnecctioon iinsttancce = nuull; prrivaate staaticc Sttrinng ddri
28、vverCClasssNaame = nnulll; prrivaate staaticc Sttrinng cconnnecttionnUrll = nulll; prrivaate staaticc Sttrinng uuserrNamme = nuull; prrivaate staaticc Sttrinng ppasssworrd = nuull; prrivaate staaticc Coonneectiion connn = nuull; prrivaate staaticc Prropeertiies jdbbcPrrop = nnulll; prrivaate DBCConn
29、necttionn() prrivaate staaticc Prropeertiies gettConnfiggFroomPrropeertiiesFFilee() thrrowss Exxcepptioon PPropperttiess prrop = nnulll; ppropp = JdbbcPrropeertiies.gettProopObbjFrromFFilee(); rretuurn proop; prrivaate staaticc vooid iniitJddbcPParaametterss(Prropeertiies proop) ddrivverCClasssNaame
30、 = ppropp.geetPrropeertyy(Coonsttantts.DDRIVVER_CLAASS_NAMME); cconnnecttionnUrll = proop.ggetPProppertty(CConsstannts.CONNNECCTIOON_UURL); uuserrNamme = prrop.gettProoperrty(Connstaantss.DBB_USSER_NAMME); ppasssworrd = prrop.gettProoperrty(Connstaantss.DBB_USSER_PASSSWOORD); prrivaate staaticc vooi
31、d creeateeConnnecctioon() thhrowws EExceeptiion CClasss.fforNNamee(drriveerCllasssNamme); cconnn = DriiverrMannageer.ggetCConnnecttionn(coonneectiionUUrl, usserNNamee, ppasssworrd); puubliic sstattic Connnecctioon ggetCConnnecttionn() thrrowss Exxcepptioon rretuurn connn; puubliic ssyncchroonizzed s
32、taaticc DBBConnnecctioon ggetIInsttancce()thrrowss Exxcepptioon iif (insstannce = nulll) jjdbccProop = geetCoonfiigFrromPPropperttiessFille(); iinsttancce = neew DDBCoonneectiion(); iinittJdbbcPaarammeteers(jdbbcPrrop); ccreaateCConnnecttionn(); rretuurn insstannce; 2.6.33 JddbcPPropperttiess类packaa
33、ge skyy.orrg.uutill.dbb;imporrt jjavaa.ioo.Fiile;imporrt jjavaa.ioo.FiileIInpuutSttreaam;imporrt jjavaa.ioo.FiileNNotFFounndExxcepptioon;imporrt jjavaa.ioo.IOOExccepttionn;imporrt jjavaa.ioo.InnputtStrreamm;imporrt jjavaa.neet.UURL;imporrt jjavaa.uttil.*;publiic cclasss JJdbccProoperrtiees prrivaate staaticc Loog lloggger = LLogFFacttoryy.geetLoog(JJdbccProoperrtiees.cclasss); puubliic sstattic Prooperrtiees ggetPProppObjjFroomFiile() PPropperttiess obbjPrrop = nnew Prooperrtiees(); CClasssLooadeer cclasssLooadeer = Thhreaad.ccurrrenttThrreadd
限制150内