??xml version="1.0" encoding="utf-8" standalone="yes"?>中文字幕日韩精品有码视频,丁香5月婷婷久久,成人激情avhttp://www.aygfsteel.com/Ericzhang5231/category/29928.htmlzh-cnFri, 14 Mar 2008 18:20:39 GMTFri, 14 Mar 2008 18:20:39 GMT60Java ThreadLocal介绍http://www.aygfsteel.com/Ericzhang5231/articles/JavaThreadlocal.htmlEric5231Eric5231Thu, 13 Mar 2008 08:54:00 GMThttp://www.aygfsteel.com/Ericzhang5231/articles/JavaThreadlocal.htmlhttp://www.aygfsteel.com/Ericzhang5231/comments/186093.htmlhttp://www.aygfsteel.com/Ericzhang5231/articles/JavaThreadlocal.html#Feedback0http://www.aygfsteel.com/Ericzhang5231/comments/commentRss/186093.htmlhttp://www.aygfsteel.com/Ericzhang5231/services/trackbacks/186093.html概述

我们知道Spring通过各种DAO模板c降低了开发者用各U数据持久技术的隑ֺ。这些模板类都是U程安全的,也就是说Q多个DAO可以复用同一个模板实例而不会发生冲H?/p>

我们使用模板c访问底层数据,Ҏ持久化技术的不同Q模板类需要绑定数据连接或会话的资源。但q些资源本n是非U程安全的,也就是说它们不能在同一时刻被多个线E共享?/p>

虽然模板c通过资源池获取数据连接或会话Q但资源池本w解决的是数据连接或会话的缓存问题,q数据q接或会话的U程安全问题?/p>

按照传统l验Q如果某个对象是非线E安全的Q在多线E环境下Q对对象的访问必采用synchronizedq行U程同步。但Spring的DAO模板cdƈ未采用线E同步机Ӟ因ؓU程同步限制了ƈ发访问,会带来很大的性能损失?/p>

此外Q通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现隑ֺ。那模板cȝ竟Ԓ丈何U魔法神功,可以在无需同步的情况下化解线E安全的N呢?{案是ThreadLocalQ?/p>

ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、Q务调度、AOP{模块都出现了它们的w媄Qv着举轻重的作用。要想了解Spring事务理的底层技术,ThreadLocal是必L克的山头堡垒?/p>

ThreadLocal是什?/strong>

早在JDK 1.2的版本中提供java.lang.ThreadLocalQThreadLocal军_U程E序的ƈ发问题提供了一U新的思\。用这个工L可以很简z地~写Z的多线E程序?/p>

ThreadLocal很容易让人望文生义,惛_然地认ؓ是一?#8220;本地U程”。其实,ThreadLocalq不是一个ThreadQ而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些?/p>

当用ThreadLocall护变量ӞThreadLocal为每个用该变量的线E提供独立的变量副本Q所以每一个线E都可以独立地改变自q副本Q而不会媄响其它线E所对应的副本?/p>

从线E的角度看,目标变量p是线E的本地变量Q这也是cd?#8220;Local”所要表辄意思?/p>

U程局部变量ƈ不是Java的新发明Q很多语aQ如IBM IBM XL FORTRANQ在语法层面提供线E局部变量。在Java中没有提供在语言U支持,而是变相地通过ThreadLocal的类提供支持?/p>

所以,在Java中编写线E局部变量的代码相对来说要笨拙一些,因此造成U程局部变量没有在Java开发者中得到很好的普及?/p>

ThreadLocal的接口方?/p>

ThreadLocalcL口很单,只有4个方法,我们先来了解一下:

  • void set(Object value)

讄当前U程的线E局部变量的倹{?/p>

  • public Object get()

该方法返回当前线E所对应的线E局部变量?/p>

  • public void remove()

当前线E局部变量的值删除,目的是ؓ了减内存的占用Q该Ҏ是JDK 5.0新增的方法。需要指出的是,当线E结束后Q对应该U程的局部变量将自动被垃圑֛Ӟ所以显式调用该Ҏ清除U程的局部变量ƈ不是必须的操作,但它可以加快内存回收的速度?/p>

  • protected Object initialValue()

q回该线E局部变量的初始|该方法是一个protected的方法,昄是ؓ了让子类覆盖而设计的。这个方法是一个gq调用方法,在线E第1ơ调用get()或set(Object)时才执行Qƈ且仅执行1ơ。ThreadLocal中的~省实现直接q回一个null?/p>

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型Q该cȝcd已经变ؓThreadLocal<T>。APIҎ 也相应进行了调整Q新版本的APIҎ分别是void set(T value)、T get()以及T initialValue()?/p>

ThreadLocal是如何做Cؓ每一个线E维护变量的副本的呢Q其实实现的思\很简单:在ThreadLocalcM有一个MapQ用于存储每一个线E的变量副本QMap中元素的键ؓU程对象Q而值对应线E的变量副本。我们自己就可以提供一个简单的实现版本Q?/p>

代码清单1 SimpleThreadLocal

public class SimpleThreadLocal {

private Map valueMap = Collections.synchronizedMap(new HashMap());

public void set(Object newValue) {

valueMap.put(Thread.currentThread(), newValue);①键为线E对象,gؓ本线E的变量副本

}

public Object get() {

Thread currentThread = Thread.currentThread();

Object o = valueMap.get(currentThread);②返回本U程对应的变?/strong>

if (o == null && !valueMap.containsKey(currentThread)) {③如果在Map中不存在Q放到Map

中保存v来?/strong>

o = initialValue();

valueMap.put(currentThread, o);

}

return o;

}

public void remove() {

valueMap.remove(Thread.currentThread());

}

public Object initialValue() {

return null;

}

}

虽然代码清单9?q个ThreadLocal实现版本昑־比较q稚Q但它和JDK所提供的ThreadLocalcd实现思\上是相近的?/p>

一个TheadLocal实例

下面Q我们通过一个具体的实例了解一下ThreadLocal的具体用方法?/p>

代码清单2 SequenceNumber

package com.baobaotao.basic;

public class SequenceNumber {

?/strong>通过匿名内部c覆?/strong>ThreadLocal?/strong>initialValue()ҎQ指定初始?/strong>

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

public Integer initialValue(){

return 0;

}

};

?/strong>获取下一个序列?/strong>

public int getNextNum(){

seqNum.set(seqNum.get()+1);

return seqNum.get();

}

public static void main(String[] args)

{

SequenceNumber sn = new SequenceNumber();

?/strong> 3个线E共?/strong>snQ各自生序列号

TestClient t1 = new TestClient(sn);

TestClient t2 = new TestClient(sn);

TestClient t3 = new TestClient(sn);

t1.start();

t2.start();

t3.start();

}

private static class TestClient extends Thread

{

private SequenceNumber sn;

public TestClient(SequenceNumber sn) {

this.sn = sn;

}

public void run()

{

for (int i = 0; i < 3; i++) {④每个线E打?/strong>3个序列?/strong>

System.out.println("thread["+Thread.currentThread().getName()+

"] sn["+sn.getNextNum()+"]");

}

}

}

}

通常我们通过匿名内部cȝ方式定义ThreadLocal的子c,提供初始的变量|如例子中①处所C。TestClientU程产生一l序列号Q? 在③处,我们生成3个TestClientQ它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输Z下的l果Q?/p>

thread[Thread-2] sn[1]

thread[Thread-0] sn[1]

thread[Thread-1] sn[1]

thread[Thread-2] sn[2]

thread[Thread-0] sn[2]

thread[Thread-1] sn[2]

thread[Thread-2] sn[3]

thread[Thread-0] sn[3]

thread[Thread-1] sn[3]

考察输出的结果信息,我们发现每个U程所产生的序可焉׃n同一个SequenceNumber实例Q但它们q没有发生相互干扰的情况Q而是各自产生独立的序列号Q这是因为我们通过ThreadLocal为每一个线E提供了单独的副本?/p>

Thread同步机制的比?/p>

ThreadLocal和线E同步机制相比有什么优势呢QThreadLocal和线E同步机刉是ؓ了解军_U程中相同变量的讉K冲突问题?/p>

在同步机制中Q通过对象的锁机制保证同一旉只有一个线E访问变量。这时该变量是多个线E共享的Q用同步机制要求程序慎密地分析什么时候对变量q行dQ什么时候需要锁定某个对象,什么时候释攑֯象锁{繁杂的问题Q程序设计和~写隑ֺ相对较大?/p>

而ThreadLocal则从另一个角度来解决多线E的q发讉K。ThreadLocal会ؓ每一个线E提供一个独立的变量副本Q从而隔M多个U? E对数据的访问冲H。因为每一个线E都拥有自己的变量副本,从而也没有必要对该变量进行同步了。ThreadLocal提供了线E安全的׃n对象Q在~? 写多U程代码Ӟ可以把不安全的变量封装进ThreadLocal?/p>

׃ThreadLocal中可以持有Q何类型的对象Q低版本JDK所提供的get()q回的是Object对象Q需要强制类型{换。但JDK 5.0通过泛型很好的解决了q个问题Q在一定程度地化ThreadLocal的用,代码清单 9 2׃用了JDK 5.0新的ThreadLocal<T>版本?/p>

概括h_对于多线E资源共享的问题Q同步机刉用了“以时间换I间”的方式,而ThreadLocal采用?#8220;以空间换旉”的方式。前者仅提供一份变量,让不同的U程排队讉KQ而后者ؓ每一个线E都提供了一份变量,因此可以同时讉K而互不媄响?/p>

Spring使用ThreadLocal解决U程安全问题

我们知道在一般情况下Q只有无状态的Bean才可以在多线E环境下׃nQ在Spring中,l大部分Bean都可以声明ؓsingleton作用 域。就是因为Spring对一些BeanQ如RequestContextHolder? TransactionSynchronizationManager、LocaleContextHolder{)中非U程安全状态采? ThreadLocalq行处理Q让它们也成为线E安全的状态,因ؓ有状态的Bean可以在多线E中׃n了?/p>

一般的Web应用划分为展现层、服务层和持久层三个层次Q在不同的层中编写对应的逻辑Q下层通过接口向上层开攑֊能调用。在一般情况下Q从接收h到返回响应所l过的所有程序调用都同属于一个线E,如图9?所C:

通通透透理解ThreadLocal

?/a>1同一U程贯通三?/p>

q样你就可以Ҏ需要,一些非U程安全的变量以ThreadLocal存放Q在同一ơ请求响应的调用U程中,所有关联的对象引用到的都是同一个变量?/p>

下面的实例能够体现SpringҎ状态Bean的改造思\Q?/p>

代码清单3 TopicDaoQ非U程安全

public class TopicDao {

private Connection conn;?/strong>一个非U程安全的变?/strong>

public void addTopic(){

Statement stat = conn.createStatement();?/strong>引用非线E安全变?/strong>

}

}

׃①处的conn是成员变量,因ؓaddTopic()Ҏ是非U程安全的,必须在用时创徏一个新TopicDao实例Q非singletonQ。下面用ThreadLocal对connq个非线E安全的“状?#8221;q行攚w:

代码清单4 TopicDaoQ线E安?/p>

import java.sql.Connection;

import java.sql.Statement;

public class TopicDao {

①用ThreadLocal保存Connection变量

private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

public static Connection getConnection(){

②如果connThreadLocal没有本线E对应的Connection创徏一个新的ConnectionQ?/strong>

q将其保存到U程本地变量中?/strong>

if (connThreadLocal.get() == null) {

Connection conn = ConnectionManager.getConnection();

connThreadLocal.set(conn);

return conn;

}else{

return connThreadLocal.get();③直接返回线E本地变?/strong>

}

}

public void addTopic() {

④从ThreadLocal中获取线E对应的Connection

Statement stat = getConnection().createStatement();

}

}

不同的线E在使用TopicDaoӞ先判断connThreadLocal.get()是否是nullQ如果是nullQ则说明当前U程q没有对 应的Connection对象Q这时创Z个Connection对象q添加到本地U程变量中;如果不ؓnullQ则说明当前的线E已l拥有了 Connection对象Q直接用就可以了。这P׃证了不同的线E用线E相关的ConnectionQ而不会用其它线E的 Connection。因此,q个TopicDao可以做到singleton׃n了?/p>

当然Q这个例子本w很_糙Q将Connection的ThreadLocal直接攑֜DAO只能做到本DAO的多个方法共享Connection? 不发生线E安全问题,但无法和其它DAOq同一个ConnectionQ要做到同一事务多DAO׃n同一ConnectionQ必d一个共同的外部c? 使用ThreadLocal保存Connection。但q个实例基本上说明了SpringҎ状态类U程安全化的解决思\?/p>

ThreadLocal是解决线E安全问题一个很好的思\Q它通过为每个线E提供一个独立的变量副本解决了变量ƈ发访问的冲突问题。在很多情况下, ThreadLocal比直接用synchronized同步机制解决U程安全问题更简单,更方便,且结果程序拥有更高的q发性?/p>

Eric5231 2008-03-13 16:54 发表评论
]]>
վ֩ģ壺 | | | | | ɽ| ˷| ɽ| | | | | | | | | | | ̩| | Դ| Ҷ| ̨| Ͳ| | | | | | ԭ| | ͨ| ɽ| ػʵ| | | | | | | |