路漫漫其修远兮,吾将上下而求索
经验浅薄,耐心积累;记性不好,记诸文字
BlogJava
首页
新随笔
联系
聚合
管理
随笔-204 评论-149 文章-0 trackbacks-0
正确理解ThreadLocal
转:
http://www.javaeye.com/post/504793?page=1
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。
各个线程中访问的是不同的对象。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。
通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
下面来看一个hibernate中典型的ThreadLocal的应用:
1
private
static
final
ThreadLocal threadSession
=
new
ThreadLocal();
2
3
public
static
Session getSession()
throws
InfrastructureException
{
4
Session s
=
(Session) threadSession.get();
5
try
{
6
if
(s
==
null
)
{
7
s
=
getSessionFactory().openSession();
8
threadSession.set(s);
9
}
10
}
catch
(HibernateException ex)
{
11
throw
new
InfrastructureException(ex);
12
}
13
return
s;
14
}
可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。
总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
当然如果要把本来线程共享的对象通过ThreadLocal.set()放到线程中也可以,可以实现避免参数传递的访问方式,但是要注意get()到的是那同一个共享对象,并发访问问题要靠其他手段来解决。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。
ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。
posted on 2009-06-02 14:18
Frank_Fang
阅读(398)
评论(3)
编辑
收藏
所属分类:
Java编程
评论:
#
re: 正确理解ThreadLocal 2009-07-01 20:17 |
Frank_Fang
package
test.thread;
import
java.util.Random;
/** */
/**
* ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性
* ThreadLocal的实现本来就比较简单,只是用threadLocal变量来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少同一个线程中的参数的传递。
*
*
* 在同一个线程内,完全不相关的两个段代码、函数之间如何共享一个变量呢?通过ThreadLocal可以做到
* 而且这两段代码之间不用显式的传递参数,降低了耦合
*
* ThreadLocal类似一个 Thread Context,减少调用、传参复杂度,增加环境依赖。
*
* ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
* 同一个线程内的资源本来就是共享的,只是增加了使用的方便性,避免通过方法传递参数就是他的优点!
*
*/
/** */
/**
* 如果一个类中定义了一个static的ThreadLocal,一个共享对象可以通过该ThreadLocal的set设置到多个线程的ThreadLocalMap中,但是这多个线程的ThreadLocalMap中存着的仅仅是该对象的引用,指向那个共享对象,而不是什么副本,通过ThreadLocal的get方法取到的是就是那个共享对象本身,共享访问安全问题还是要靠其他方法来解决。而实际中是不会这样使用的,很显然,这个共享变量是需要同步的(如果是线程之间的共享对象,那么其引用根本没有必要放在线程中,需要同步)
ThreadLocalMap在每个线程中有一个,而不是存在于ThreadLocal中,ThreadLocal更多是作为一个工具类,里面只包含一个int threadLocalHashCode,而不包含其他任何数据,数据是放在每个线程的ThreadLocalMap中的,里面存放的是你要通过ThreadLocal进行set和get的对象(引用),threadLocalHashCode相当于这个Map的key。
如果一个类中定义了多个ThreadLocal,那么这些个ThreadLocal中的threadLocalHashCode值是不同的,也就是key不同,所以可以用来将不同的多个对象放到线程中。
考虑一个类的多线程环境,对于该类中的static的某个ThreadLocal对象,在多个线程中是同一个对象,同一个threadLocalHashCode值,也就是同一个key,但是不同的是每个线程中的ThreadLocalMap,每个线程都有自己的ThreadLocalMap,所以相同的key可以对应不同的对象。
说到底,ThreadLocal的作用就是将经常要用到的对象的引用放到属于线程自己的一个存储空间中,在该线程的执行过程中,可以通过类的静态的ThreadLocal来方便的获取到这个对象,而不用通过参数的形式传来传去。
*/
/** */
/**
* 首先要能清楚为什么要使用ThreadLocal,如果没有ThreadLocal,能不能解决问题。
没有ThreadLocal的话,每个Thread中都有输入自己的一个本地变量,但是在整个Thread的生命中,如果要穿梭很多class的很多method来使用这个本地变量的话,就要一直一直向下传送这个变量,显然很麻烦。
那么怎么才能在这个Thread的生命中,在任何地方都能够方便的访问到这个变量呢,这时候ThreadLocal就诞生了。
ThreadLocal就是这么个作用,除此之外和通常使用的本地变量没有任何区别。
我奇怪的是为什么非要和synchronized扯上关系,完全风马牛不相及的两个东西。
*/
//
注意其中的几行注释代码
public
class
ThreadLocalDemo
implements
Runnable
{
private
final
static
ThreadLocal studentLocal
=
new
ThreadLocal();
private
Student classStudent
=
new
Student();
public
static
void
main(String[] agrs)
{
ThreadLocalDemo td
=
new
ThreadLocalDemo();
//
td中的classStudent为多个线程共享了的
Thread t1
=
new
Thread(td,
"
a
"
);
Thread t2
=
new
Thread(td,
"
b
"
);
t1.start();
t2.start();
}
/**/
/*
(non-Javadoc)
* @see java.lang.Runnable#run()
*/
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 = getStudentNotThreadLocal();
Student student
=
getStudent();
student.setAge(age);
System.out.println(
"
thread
"
+
currentThreadName
+
"
first read age is:
"
+
student.getAge()
+
"
-----
"
+
student);
try
{
Thread.sleep(
5000
);
}
catch
(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println(
"
thread
"
+
currentThreadName
+
"
second read age is:
"
+
student.getAge()
+
"
-----
"
+
student);
}
protected
Student getStudent()
{
Student student
=
(Student)studentLocal.get();
if
(student
==
null
)
{
student
=
new
Student();
//
student = classStudent;
studentLocal.set(student);
}
return
student;
}
protected
void
setStudent(Student student)
{
studentLocal.set(student);
}
protected
Student getStudentNotThreadLocal()
{
Student student
=
new
Student();
return
student;
}
}
回复
更多评论
#
re: 正确理解ThreadLocal 2009-07-01 20:27 |
Frank_Fang
public
T get()
{
Thread t
=
Thread.currentThread();
ThreadLocalMap map
=
getMap(t);
if
(map
!=
null
)
{
ThreadLocalMap.Entry e
=
map.getEntry(
this
);
if
(e
!=
null
)
return
(T)e.value;
}
return
setInitialValue();
}
public
void
set(T value)
{
Thread t
=
Thread.currentThread();
ThreadLocalMap map
=
getMap(t);
if
(map
!=
null
)
map.set(
this
, value);
else
createMap(t, value);
}
ThreadLocal的get和set方法的源码
回复
更多评论
#
re: 正确理解ThreadLocal
2009-07-01 20:30 |
Frank_Fang
package
edu.bupt.vo;
import
org.hibernate.HibernateException;
import
org.hibernate.Session;
import
org.hibernate.cfg.Configuration;
/** */
/**
* Configures and provides access to Hibernate sessions, tied to the
* current thread of execution. Follows the Thread Local Session
* pattern, see {
@link
http://hibernate.org/42.html
}.
*/
public
class
HibernateSessionFactory
{
/** */
/**
* Location of hibernate.cfg.xml file.
* Location should be on the classpath as Hibernate uses
* #resourceAsStream style lookup for its configuration file.
* The default classpath location of the hibernate config file is
* in the default package. Use #setConfigFile() to update
* the location of the configuration file for the current session.
*/
private
static
String CONFIG_FILE_LOCATION
=
"
/hibernate.cfg.xml
"
;
private
static
final
ThreadLocal
<
Session
>
threadLocal
=
new
ThreadLocal
<
Session
>
();
private
static
Configuration configuration
=
new
Configuration();
private
static
org.hibernate.SessionFactory sessionFactory;
private
static
String configFile
=
CONFIG_FILE_LOCATION;
static
{
try
{
configuration.configure(configFile);
sessionFactory
=
configuration.buildSessionFactory();
}
catch
(Exception e)
{
System.err
.println(
"
%%%% Error Creating SessionFactory %%%%
"
);
e.printStackTrace();
}
}
private
HibernateSessionFactory()
{
}
/** */
/**
* Returns the ThreadLocal Session instance. Lazy initialize
* the <code>SessionFactory</code> if needed.
*
*
@return
Session
*
@throws
HibernateException
*/
public
static
Session getSession()
throws
HibernateException
{
Session session
=
(Session) threadLocal.get();
if
(session
==
null
||
!
session.isOpen())
{
if
(sessionFactory
==
null
)
{
rebuildSessionFactory();
}
//
sessionFactory.openSession()是new 了一个新的session
session
=
(sessionFactory
!=
null
)
?
sessionFactory.openSession()
:
null
;
threadLocal.set(session);
}
return
session;
}
/** */
/**
* Rebuild hibernate session factory
*
*/
public
static
void
rebuildSessionFactory()
{
try
{
configuration.configure(configFile);
sessionFactory
=
configuration.buildSessionFactory();
}
catch
(Exception e)
{
System.err
.println(
"
%%%% Error Creating SessionFactory %%%%
"
);
e.printStackTrace();
}
}
/** */
/**
* Close the single hibernate session instance.
*
*
@throws
HibernateException
*/
public
static
void
closeSession()
throws
HibernateException
{
Session session
=
(Session) threadLocal.get();
threadLocal.set(
null
);
if
(session
!=
null
)
{
session.close();
}
}
/** */
/**
* return session factory
*
*/
public
static
org.hibernate.SessionFactory getSessionFactory()
{
return
sessionFactory;
}
/** */
/**
* return session factory
*
* session factory will be rebuilded in the next call
*/
public
static
void
setConfigFile(String configFile)
{
HibernateSessionFactory.configFile
=
configFile;
sessionFactory
=
null
;
}
/** */
/**
* return hibernate configuration
*
*/
public
static
Configuration getConfiguration()
{
return
configuration;
}
}
回复
更多评论
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
知识库
C++博客
博问
管理
相关文章:
t.interrupt(),t.isInterrupted(),Thread.interrupted()
【转】线程中的默认异常处理
Java中主线程如何捕获子线程抛出的异常
【转】Java1.5泛型指南中文版(Java1.5 Generic Tutorial)
Java集合类小结
Java反射机制学习小结
Java Hashtable分析
Java 内存模型及 volatile关键字语义
[转]J2EE项目异常处理
Java transient关键字
<
2009年6月
>
日
一
二
三
四
五
六
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
常用链接
我的随笔
我的评论
我的参与
最新评论
留言簿
(1)
给我留言
查看公开留言
查看私人留言
随笔分类
(204)
Andriod(2)
bcel javassist(9)
C++编程(23)
Design Pattern(36)
JAVA WS(16)
Java 网络编程(1)
Java编程(44)
JNI(1)
Linux | ACE网络编程(13)
Python学习(4)
SSH+JQuery+DWR(39)
数据结构与算法(12)
笔试,面试经验(4)
随笔档案
(100)
2009年8月 (17)
2009年7月 (21)
2009年6月 (21)
2009年5月 (32)
2009年4月 (9)
收藏夹
(8)
牛人博客文章链接(8)
牛人博客链接
搜索
最新评论
1. re: Java transient关键字[未登录]
@AlexSeeker
volatile屏蔽了重排序优化
--aa
2. re: Java transient关键字
评论内容较长,点击标题查看
--333
3. re: Java transient关键字
555
--55
4. re: Java transient关键字
很不错。
--seancheer
5. re: Java Serializable小结
过来看看
--vacon
阅读排行榜
1. Java transient关键字(110462)
2. Struts 注解配置例子及redirect,redirectAction,chain的区别(25200)
3. static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?(18075)
4. Java多线程sleep(),join(),interrupt(),wait(),notify()(12784)
5. 线程同步:何时互斥锁不够,还需要条件变量?(9205)
评论排行榜
1. Java transient关键字(26)
2. 【转】用 BCEL 设计字节码(7)
3. Struts 注解配置例子及redirect,redirectAction,chain的区别(6)
4. Java 内存模型及 volatile关键字语义(5)
5. Java多线程sleep(),join(),interrupt(),wait(),notify()(5)