路漫漫其修遠兮,吾將上下而求索
經驗淺薄,耐心積累;記性不好,記諸文字
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
閱讀(405)
評論(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新聞
Chat2DB
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關鍵字(110483)
2.?Struts 注解配置例子及redirect,redirectAction,chain的區別(25241)
3.?static全局變量與普通的全局變量有什么區別?static局部變量和普通局部變量有什么區別?static函數與普通函數有什么區別?(18085)
4.?Java多線程sleep(),join(),interrupt(),wait(),notify()(12815)
5.?線程同步:何時互斥鎖不夠,還需要條件變量?(9241)
評論排行榜
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)
Powered by:
博客園
模板提供:
滬江博客
Copyright ©2025 Frank_Fang
主站蜘蛛池模板:
亚洲日本成本人观看
|
免费黄色网址入口
|
深夜免费在线视频
|
亚洲国产区男人本色在线观看
|
亚洲AV无码乱码在线观看裸奔
|
亚洲成a人片在线观看日本麻豆
|
成人午夜免费福利
|
久久综合国产乱子伦精品免费
|
免费国产黄网站在线看
|
亚洲精品国产精品
|
亚洲av产在线精品亚洲第一站
|
亚洲Av无码专区国产乱码DVD
|
国产亚洲精品仙踪林在线播放
|
亚洲成人黄色在线观看
|
伊人久久综在合线亚洲2019
|
国产亚洲?V无码?V男人的天堂
|
亚洲天天做日日做天天欢毛片
|
亚洲中文字幕无码久久精品1
|
国产伦精品一区二区三区免费下载
|
无人在线直播免费观看
|
91在线手机精品免费观看
|
久久国产精品国产自线拍免费
|
中文字幕免费在线看线人动作大片
|
午夜亚洲国产精品福利
|
亚洲AV性色在线观看
|
亚洲AV日韩AV永久无码色欲
|
亚洲熟女www一区二区三区
|
2020天堂在线亚洲精品专区
|
亚洲区视频在线观看
|
亚洲国产成+人+综合
|
亚洲人成在久久综合网站
|
亚洲日韩中文字幕天堂不卡
|
亚洲福利视频网址
|
亚洲午夜久久久精品电影院
|
国产日本亚洲一区二区三区
|
亚洲乱码一二三四区乱码
|
亚洲熟妇AV乱码在线观看
|
亚洲av无码成人精品国产
|
国产亚洲精品成人久久网站
|
免费一级毛片在线播放视频免费观看永久
|
日韩在线视频播放免费视频完整版
|