您的位置:首页 > 编程语言 > Java开发

深入探讨java.lang.ThreadLocal类

2009-12-13 23:10 344 查看
深入研究java.lang.ThreadLocal种

1、概述

ThreadLocal是什么呢?实则ThreadLocal并非是一个线程的本土兑现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。或许把它命名为ThreadLocalVar愈加适合。线程局部变量(ThreadLocal)实则的作用十分简略,乃是为每一个施用该变量的线程都提供一个变量值的复本,是Java中一种较为非一般的线程绑定机制,是每一个线程都可以独力地改变自个儿的抄本,而不会和其它线程的复本矛盾。

从线程的视角观,每个线程都护持一个对其线程局部变量抄本的隐式引述,只要线程是活动的而且 ThreadLocal 范例是可访问的;在线程消失以后,其线程局部范例的全部抄本都市被渣滓回收(除非存在对这些复本的其余摘引)。

透过ThreadLocal存取的数据,老是与现阶段线程有关,来讲,JVM 为每个运作的线程,绑定了私有的本土范例存取空间,故而为多线程环境常出现的并发访问问题提供了一种隔绝机制。

ThreadLocal是何以作到为每一个线程维护变量的抄本的呢?实则兑现的思路很简单,在ThreadLocal种中有一个Map,用来储存每一个线程的变量的复本。

总括起床说,关于多线程资源共享的问题,同步机制采取了“以时间换空间”的模式,而ThreadLocal采取了“以空间换时间”的形式。前者仅提供一份变量,让不同的线程列队访问,尔后者为每一个线程都提供了一份变量,因而可以与此同时访问而互不影响。

2、API说明

ThreadLocal()
创设一个线程本土变量。

T get()
回到此线程局部变量确当战线程抄本中的值,如其这是线程第一回调用该步骤,则创设并初始化此复本。

protected T initialValue()
回来此线程局部变量确当战线程的初步值。至多在历次访问线程来取得每个线程局部变量时调用此步骤一次,即线程第一回施用 get() 步骤访问变量的时分。如若线程先于 get 步骤调用 set(T) 步骤,则不会在线程中再调用 initialValue 步骤。

若该兑现只回到 null;如若程序员希望将线程局部变量初始化作 null 之外的某个值,则务必为 ThreadLocal 创办子类,并重写此步骤。正常,将施用匿名内类型。initialValue 的典型兑现将调用一个适当的结构步骤,并回到新结构的对象。

void remove()
移除此线程局部变量的值。这可能有助于减小线程局部变量的储存需求。如其再度访问此线程局部变量,那么在默许情况下它将拥有其 initialValue。

void set(T value)
将此线程局部变量确当战线程复本中的值设立为指定值。好多应用程序不需要这项效能,它们只依赖于 initialValue() 步骤来设立线程局部变量的值。

在程序中正常都重写initialValue步骤,以给定一个特定的初步值。

3、典型范例

一、Hiberante的Session 工具种HibernateUtil
这个类是Hibernate官方文档中HibernateUtil种,用以session治理。

public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory; //定义SessionFactory

static {
try {
// 经过默许配置文件hibernate.cfg.xml创造SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
log.error("初始化SessionFactory失败!", ex);
throw new ExceptionInInitializerError(ex);
}
}

//创办线程局部变量session,用以封存Hibernate的Session
public static final ThreadLocal session = new ThreadLocal();

/**
* 获取目前线程中的Session
* @return Session
* @throws HibernateException
*/
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// 如其Session还没有打开,则新开一个Session
if (s == null) {
s = sessionFactory.openSession();
session.set(s); //将新开的Session保留到线程局部变量中
}
return s;
}

public static void closeSession() throws HibernateException {
//获取线程局部变量,并挟制变换为Session部类
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}

在这个类中,因为没重写ThreadLocal的initialValue()步骤,则首度创办线程局部变量session其初步值为null,第一回调用currentSession()的时分,线程局部变量的get()步骤也为null。因而,对session做了判断,如若为null,则新开一个Session,并保留到线程局部变量session中,这一步十分的要害,这也是“public static final ThreadLocal session = new ThreadLocal()”所创造对象session会挟制变换为Hibernate Session对象的缘故。

二、除此而外一个范例
创办一个Bean,经过不同的线程对象设立Bean属性,责任书各个线程Bean对象的独立性。

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 十:45:02
* 学生
*/
public class Student {
private int age = 零; //岁数

public int getAge() {
return this.age;
}

public void setAge(int age) {
this.age = age;
}
}

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 十:53:33
* 多线程下测试程序
*/
public class ThreadLocalDemo implements Runnable {
//创造线程局部变量studentLocal,在后面你会发现用以保留Student对象
private final static ThreadLocal studentLocal = new ThreadLocal();

public static void main(String[] agrs) {
ThreadLocalDemo td = new ThreadLocalDemo();
Thread t一 = new Thread(td, "a");
Thread t二 = new Thread(td, "b");
t一.start();
t二.start();
}

public void run() {
accessStudent();
}

/**
* 示范业务步骤,用于测试
*/
public void accessStudent() {
//获取现阶段线程的名字
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running!");
//发生一个随机数并打印
Random random = new Random();
int age = random.nextInt(100);
System.out.println("thread " + currentThreadName + " set age to:" + age);
//获取一个Student对象,并将随机数岁数安插到对象属性中
Student student = getStudent();
student.setAge(age);
System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
try {
Thread.sleep(500);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
}

protected Student getStudent() {
//获取当地线程变量并挟制变换为Student部类
Student student = (Student) studentLocal.get();
//线程首度实施此步骤的时分,studentLocal.get()肯定为null
if (student == null) {
//创办一个Student对象,并封存到当地线程变量studentLocal中
student = new Student();
studentLocal.set(student);
}
return student;
}
}

运作结果:
a is running!
thread a set age to:76
b is running!
thread b set age to:27
thread a first read age is:76
thread b first read age is:27
thread a second read age is:76
thread b second read age is:27

可以看到a、b两个线程age在不与此同时刻打印的值是完全相同的。这个程序透过妙用ThreadLocal,既兑现多线程并发,逛统筹数据的安全性。

4、小结

ThreadLocal施用处所重要解决多线程中数据数据因并发发生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个复本,透过访问复本来运作业务,这么的结果是消耗了内存储器,单大大减小了线程同步所带到性能耗费,也减小了线程并发统制的复杂度。

ThreadLocal不能运用原子部类,只得运用Object部类。ThreadLocal的运用比synchronized要简略得多。

ThreadLocal和Synchonized都用以解决多线程并发访问。但是ThreadLocal与synchronized有性质的差异。synchronized是利用锁的机制,使变量或代码块在某1时该不得不被一个线程访问。而ThreadLocal为每一个线程都提供了变量的抄本,使得每个线程在某一时间访问到的并不是同一个对象,这么就隔绝了多个线程对数据的数据共享。而Synchronized却恰好相反,它用以在多个线程间通讯时能够取得数据共享。

Synchronized用以线程间的数据共享,而ThreadLocal则用来线程间的数据隔绝。

当然ThreadLocal并不能顶替synchronized,它们处置不同的问题域。Synchronized用以兑现同步机制,比ThreadLocal愈加复杂。

5、ThreadLocal施用的正常方法

一、在多线程的种(如ThreadDemo种)中,创造一个ThreadLocal对象threadXxx,用于封存线程间急需隔绝处置的对象xxx。
二、在ThreadDemo种中,创造一个获取要隔绝访问的数据的步骤getXxx(),在步骤中判断,若ThreadLocal对象为null时分,应当new()一个隔绝访问门类的对象,并挟制变换为要使用的门类。
三、在ThreadDemo种的run()步骤中,透过getXxx()步骤获取要操作的数据,这么可以军令状每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

参照文档:
JDK 官方文档
http://www.java三z.com/cwbwebhome/article/article二a/271.html?id=319
http://www.java三z.com/cwbwebhome/article/article2/2952.html?id=1648

本文来源:
我的异常网
Java Exception
Dotnet Exception
Oracle Exception

1670 - Java中异常处理的顺序是如何进行的

1671 - java中怎么获取referrer

1672 - jsp异常追踪机制

1673 - IO异常:The NetWork Adapter could not establish the connection

1674 - javax.naming.OperationNotSupportedException: Can only bind References or Referenceable objects

1675 - java中如何能声明一个方法使其不能被继承类里的方法重写(override)?

1676 - HTTP 1.1 500 intrnal server error

1677 - Automation服务器无法创建对象

1678 - 该错误可能是因为执行该操作的访问权限不足

1679 - tag contains an invalid value for the

1680 - 调用对象出现异常

1681 - System.IO.FileNotFoundException: 找不到文件或程序集名称iqipmb7s.dll,或找不到它的一个依赖项

1682 - 错误:拒绝访问.JIT调试由用户LUCKY(服务器命)ASPNET启动

1683 - Visual Studio.NET已检测到指定的WEB服务器运行的不是ASP.NET 1.1版.您将无法运行ASP.NET Web应用程序或服务

1684 - org.apache.jasper.JasperException: File WEB-INF c.tld not found

1685 - the error message string of this Throwable object if it was created with an error message string

1686 - An error occurred during the compilation of a resource required to service this request

1687 - CS0234: 类型或命名空间名称ADO在类或命名空间System.Data中不存在

1688 - 未能加载类型

1689 - 未能访问CDO.Message对象
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: