★yesjoy★
★
總在爬山 所以艱辛;總在尋夢 所以苦痛
★
BlogJava
首頁
新隨筆
聯系
聚合
管理
隨筆 - 71 文章 - 15 trackbacks - 0
<
2025年5月
>
日
一
二
三
四
五
六
27
28
29
30
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
31
1
2
3
4
5
6
7
★
因為口渴,上帝創造了水;
★
因為黑暗,上帝創造了火;
★
因為我需要朋友,所以上帝讓你來到我身邊
╱◥█◣
|田|田|
╬╬╬╬╬╬╬╬╬╬╬
If only I have such a house!
〖總在爬山 所以艱辛〗
Email:myesjoy@yahoo.com.cn
NickName:yesjoy
MSN:myesjoy@hotmail.com
QQ:150230516
〖總在尋夢 所以苦痛〗
常用鏈接
我的隨筆
我的評論
我的參與
最新評論
留言簿
(3)
給我留言
查看公開留言
查看私人留言
隨筆分類
Hibernate學習總結(1)
J2EE架構
Struts學習總結(1)
隨筆檔案
2018年8月 (1)
2007年6月 (3)
2007年5月 (8)
2007年4月 (21)
2007年3月 (6)
2007年1月 (1)
2006年12月 (3)
2006年8月 (1)
2006年4月 (1)
2006年3月 (1)
文章分類
AOP(面向方面編程)(5)
C/C++語言算法總結(16)
CORBA 學習(1)
DB2學習(1)
Hibernate學習(8)
J2EE結構(3)
java.applet包(2)
java.awt包(4)
java.util包 (2)
JAVA代碼查錯(2)
Java基礎知識(3)
Java常用類
JAVA編程規范(2)
Oracle學習(3)
PowerDesigner設計(4)
Spring學習(3)
Structs學習(15)
公眾資源(1)
存儲設備(1)
工作流基本知識(6)
常用Jar包介紹和應用(1)
數據庫備份/恢復方案(2)
數據庫的查詢及性能優化(1)
數據庫設計(4)
數據結構(1)
算法總結(7)
設計模式(4)
讀書世界(2)
軟件工程學(13)
需求管理(1)
項目經驗總結(2)
文章檔案
2014年1月 (1)
2007年8月 (10)
2007年7月 (7)
2007年6月 (19)
2007年5月 (9)
2007年4月 (3)
2007年3月 (5)
2007年1月 (3)
2006年11月 (7)
2006年10月 (6)
2006年8月 (5)
2006年7月 (1)
2006年4月 (5)
2006年3月 (7)
2006年2月 (20)
2006年1月 (14)
Hibernate在線
HIBERNATE - 符合Java習慣的關系數據庫持久化
Java友情
§BlogJava - 鐵手劍譜§
§oksonic博客§
§sampa : colin’s blog§
§人生代碼§
Java認證
IT認證傳奇站
JAVA SCJD認證 SCJP認證 JAVA程序設計員認證網站
linux經典
‖LinuxABC.NET‖
Linux菜鳥到高手,就在LinuxABC.NET
OA系統
¤易能協同辦公系統¤
流程管理、知識管理、客戶關系管理、輔助辦公
¤黃城網絡辦公系統3.0¤
B/S結構,適用于Intranet/Internet應用,實現無地域限制的全球辦公,具有郵件管理、業務管理、網絡硬盤、智能工作流等功能。
Spring在線
【Spring Framework 開發參考手冊】
Structs在線
【IBM_Struts 應用專題】
【Structs官方網站】
專家專欄
§張孝祥專欄_Java基礎§
§芮祥麟的專欄_SOA§
企業信息化
¢ERPHome¢
¢e-works中國制造業信息化門戶¢
管理_技術_信息化
大型設備共享系統
武漢大學儀器設備共享平臺
湖南高校大型儀器設備共用網
工作流
∫中國工作流論壇∫
∫工作流管理聯盟WfMC ∫
∫工作流網∫
業務過程建模與工作流管理技術論壇
工作流產品
℃協同軟件開發者社區 :: 首頁℃
網上購書
China-Pub網上書店
卓越網: 網上購物
當當網,全球最大的中文網上商城
淘寶網
搜索
最新評論
1.?re: DetachedCriteria關聯查詢
dddd
--sss
2.?re: Struts提供的強大的HTML標簽庫總結[未登錄]
是
--哈哈
3.?re: 打印出1900~2000年中所有的閏年
不錯
--網通
4.?re: DetachedCriteria關聯查詢
@name
是有重復數據,該怎么解決啊
--996416660
5.?re: DetachedCriteria關聯查詢
這個查詢的話,會多出重復數據。
--name
閱讀排行榜
1.?在struts中如何使用showModalDialog() (1036)
2.?eclipse中進行hibernate映射的步驟(854)
3.?2006年12月份工作總結之一(602)
4.?這五天來的工作。。。(412)
5.?籃球的意義(395)
評論排行榜
1.?十多年后再發個隨筆,感覺很奇怪(0)
2.?買了移動硬盤(0)
3.?這五天來的工作。。。(0)
4.?高效工作,高效生活,我的人生!(0)
5.?安全意識在哪里?(0)
單例模式完全剖析(2)---- 探究簡單卻又使人迷惑的單例模式
測試單例模式
接下來,我使用與log4j相對應的JUnit來測試單例類,它會貫穿在這篇文章余下的部分。如果你對JUnit或log4j不很熟悉,請參考相關資源。
例2是一個用JUnit測試例1的單例模式的案例:
例2.一個單例模式的案例
import
org.apache.log4j.
Logger
;
import
junit.framework.
Assert
;
import
junit.framework.
TestCase
;
public
class
SingletonTest
extends
TestCase
{
private
ClassicSingleton sone =
null
, stwo =
null
;
private
static
Logger
logger =
Logger
.getRootLogger();
public
SingletonTest(
String
name) {
super
(name);
}
public
void
setUp() {
logger.info(
"getting singleton..."
);
sone = ClassicSingleton.getInstance();
logger.info(
"...got singleton: "
+ sone);
logger.info(
"getting singleton..."
);
stwo = ClassicSingleton.getInstance();
logger.info(
"...got singleton: "
+ stwo);
}
public
void
testUnique() {
logger.info(
"checking singletons for equality"
);
Assert
.assertEquals(
true
, sone == stwo);
}
}
例2兩次調用ClassicSingleton.getInstance(),并且把返回的引用存儲在成員變量中。方法testUnique()會檢查這些引用看它們是否相同。例3是這個測試案例的輸出:
例3.是這個測試案例的輸出
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:08)
compile:
run-test-text:
[java] .INFO main: [b]getting singleton...[/b]
[java] INFO main: [b]created singleton:[/b] Singleton@e86f41
[java] INFO main: ...got singleton: Singleton@e86f41
[java] INFO main: [b]getting singleton...[/b]
[java] INFO main: ...got singleton: Singleton@e86f41
[java] INFO main: checking singletons
for
equality
[java]
Time
: 0.032
[java] OK (1 test)
正如前面的清單所示,例2的簡單測試順利通過----通過ClassicSingleton.getInstance()獲得的兩個單例類的引用確實相同;然而,你要知道這些引用是在單線程中得到的。下面的部分著重于用多線程測試單例類。
多線程因素的考慮
在例1中的ClassicSingleton.getInstance()方法由于下面的代碼而不是線程安全的:
1:
if
(instance ==
null
) {
2: instance =
new
Singleton();
3: }
如果一個線程在第二行的賦值語句發生之前切換,那么成員變量instance仍然是null,然后另一個線程可能接下來進入到if塊中。在這種情況下,兩個不同的單例類實例就被創建。不幸的是這種假定很少發生,這樣這種假定也很難在測試期間出現(譯注:在這可能是作者對很少出現這種情況而導致無法測試從而使人們放松警惕而感到嘆惜)。為了演示這個線程輪換,我得重新實現例1中的那個類。例4就是修訂后的單例類:
例4.人為安排的方式
import
org.apache.log4j.
Logger
;
public
class
Singleton {
private
static
Singleton singleton =
null
;
private
static
Logger
logger =
Logger
.getRootLogger();
private
static
boolean
firstThread =
true
;
protected
Singleton() {
// Exists only to defeat instantiation.
}
public
static
Singleton getInstance() {
if
(singleton ==
null
) {
simulateRandomActivity();
singleton =
new
Singleton();
}
logger.info(
"created singleton: "
+ singleton);
return
singleton;
}
private
static
void
simulateRandomActivity() {
try
{
if
(firstThread) {
firstThread =
false
;
logger.info(
"sleeping..."
);
// This nap should give the second thread enough time
// to get by the first thread.
Thread
.currentThread().sleep(50);
}
}
catch
(
InterruptedException
ex) {
logger.warn(
"Sleep interrupted"
);
}
}
}
除了在這個清單中的單例類強制使用了一個多線程錯誤處理,例4類似于例1中的單例類。在getInstance()方法第一次被調用時,調用這個方法的線程會休眠50毫秒以便另外的線程也有時間調用getInstance()并創建一個新的單例類實例。當休眠的線程覺醒時,它也會創建一個新的單例類實例,這樣我們就有兩個單例類實例。盡管例4是人為如此的,但它卻模擬了第一個線程調用了getInstance()并在沒有完成時被切換的真實情形。
例5測試了例4的單例類:
例5.失敗的測試
import
org.apache.log4j.
Logger
;
import
junit.framework.
Assert
;
import
junit.framework.
TestCase
;
public
class
SingletonTest
extends
TestCase
{
private
static
Logger
logger =
Logger
.getRootLogger();
private
static
Singleton singleton =
null
;
public
SingletonTest(
String
name) {
super
(name);
}
public
void
setUp() {
singleton =
null
;
}
public
void
testUnique()
throws
InterruptedException
{
// Both threads call Singleton.getInstance().
Thread
threadOne =
new
Thread
(
new
SingletonTestRunnable()),
threadTwo =
new
Thread
(
new
SingletonTestRunnable());
threadOne.start();
threadTwo.start();
threadOne.join();
threadTwo.join();
}
private
static
class
SingletonTestRunnable
implements
Runnable
{
public
void
run() {
// Get a reference to the singleton.
Singleton s = Singleton.getInstance();
// Protect singleton member variable from
// multithreaded access.
synchronized
(SingletonTest.
class
) {
if
(singleton ==
null
)
// If local reference is null...
singleton = s;
// ...set it to the singleton
}
// Local reference must be equal to the one and
// only instance of Singleton; otherwise, we have two
// Singleton instances.
Assert
.assertEquals(
true
, s == singleton);
}
}
}
例5的測試案例創建兩個線程,然后各自啟動,等待完成。這個案例保持了一個對單例類的靜態引用,每個線程都會調用Singleton.getInstance()。如果這個靜態成員變量沒有被設置,那么第一個線程就會將它設為通過調用getInstance()而得到的引用,然后這個靜態變量會與一個局部變量比較是否相等。
在這個測試案例運行時會發生一系列的事情:第一個線程調用getInstance(),進入if塊,然后休眠;接著,第二個線程也調用getInstance()并且創建了一個單例類的實例。第二個線程會設置這個靜態成員變量為它所創建的引用。第二個線程檢查這個靜態成員變量與一個局部備份的相等性。然后測試通過。當第一個線程覺醒時,它也會創建一個單例類的實例,并且它不會設置那個靜態成員變量(因為第二個線程已經設置過了),所以那個靜態變量與那個局部變量脫離同步,相等性測試即告失敗。例6列出了例5的輸出:
例6.例5的輸出
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:06)
compile:
run-test-text:
INFO
Thread
-1: sleeping...
INFO
Thread
-2: created singleton: Singleton@7e5cbd
INFO
Thread
-1: created singleton: Singleton@704ebb
junit.framework.
AssertionFailedError
: expected: but was:
at junit.framework.
Assert
.fail(Assert.java:47)
at junit.framework.
Assert
.failNotEquals(Assert.java:282)
at junit.framework.
Assert
.assertEquals(Assert.java:64)
at junit.framework.
Assert
.assertEquals(Assert.java:149)
at junit.framework.
Assert
.assertEquals(Assert.java:155)
at SingletonTest$SingletonTestRunnable.run(Unknown
Source
)
at java.lang.
Thread
.run(
Thread
.java:554)
[java] .
[java]
Time
: 0.577
[java] OK (1 test)
到現在為止我們已經知道例4不是線程安全的,那就讓我們看看如何修正它。
同步
要使例4的單例類為線程安全的很容易----只要像下面一個同步化getInstance()方法:
public
synchronized
static
Singleton getInstance() {
if
(singleton ==
null
) {
simulateRandomActivity();
singleton =
new
Singleton();
}
logger.info(
"created singleton: "
+ singleton);
return
singleton;
}
在同步化getInstance()方法后,我們就可以得到例5的測試案例返回的下面的結果:
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:15)
compile:
[javac] Compiling 2 source files
run-test-text:
INFO
Thread
-1: sleeping...
INFO
Thread
-1: created singleton: Singleton@ef577d
INFO
Thread
-2: created singleton: Singleton@ef577d
[java] .
[java]
Time
: 0.513
[java] OK (1 test)
這此,這個測試案例工作正常,并且多線程的煩惱也被解決;然而,機敏的讀者可能會認識到getInstance()方法只需要在第一次被調用時同步。因為同步的性能開銷很昂貴(同步方法比非同步方法能降低到100次左右),或許我們可以引入一種性能改進方法,它只同步單例類的getInstance()方法中的賦值語句。
一種性能改進的方法
尋找一種性能改進方法時,你可能會選擇像下面這樣重寫getInstance()方法:
public
static
Singleton getInstance() {
if
(singleton ==
null
) {
synchronized
(Singleton.
class
) {
singleton =
new
Singleton();
}
}
return
singleton;
}
這個代碼片段只同步了關鍵的代碼,而不是同步整個方法。然而這段代碼卻不是線程安全的。考慮一下下面的假定:線程1進入同步塊,并且在它給singleton成員變量賦值之前線程1被切換。接著另一個線程進入if塊。第二個線程將等待直到第一個線程完成,并且仍然會得到兩個不同的單例類實例。有修復這個問題的方法嗎?請讀下去。
雙重加鎖檢查
初看上去,雙重加鎖檢查似乎是一種使懶漢式實例化為線程安全的技術。下面的代碼片段展示了這種技術:
public
static
Singleton getInstance() {
if
(singleton ==
null
) {
synchronized
(Singleton.
class
) {
if
(singleton ==
null
) {
singleton =
new
Singleton();
}
}
}
return
singleton;
}
如果兩個線程同時訪問getInstance()方法會發生什么?想像一下線程1進行同步塊馬上又被切換。接著,第二個線程進入if 塊。當線程1退出同步塊時,線程2會重新檢查看是否singleton實例仍然為null。因為線程1設置了singleton成員變量,所以線程2的第二次檢查會失敗,第二個單例類實例也就不會被創建。似乎就是如此。
不幸的是,雙重加鎖檢查不會保證正常工作,因為編譯器會在Singleton的構造方法被調用之前隨意給singleton賦一個值。如果在singleton引用被賦值之后而被初始化之前線程1被切換,線程2就會被返回一個對未初始化的單例類實例的引用。
一個改進的線程安全的單例模式實現
例7列出了一個簡單、快速而又是線程安全的單例模式實現:
例7.一個簡單的單例類
public
class
Singleton {
public
final
static
Singleton INSTANCE =
new
Singleton();
private
Singleton() {
// Exists only to defeat instantiation.
}
}
這段代碼是線程安全的是因為靜態成員變量一定會在類被第一次訪問時被創建。你得到了一個自動使用了懶漢式實例化的線程安全的實現;你應該這樣使用它:
Singleton singleton = Singleton.INSTANCE;
singleton.dothis();
singleton.dothat();
...
當然萬事并不完美,前面的Singleton只是一個折衷的方案;如果你使用那個實現,你就無法改變它以便后來你可能想要允許多個單例類的實例。用一種更折哀的單例模式實現(通過一個getInstance()方法獲得實例)你可以改變這個方法以便返回一個唯一的實例或者是數百個實例中的一個.你不能用一個公開且是靜態的(public static)成員變量這樣做.
你可以安全的使用例7的單例模式實現或者是例1的帶一個同步的getInstance()方法的實現.然而,我們必須要研究另一個問題:你必須在編譯期指定這個單例類,這樣就不是很靈活.一個單例類的注冊表會讓我們在運行期指定一個單例類.
posted on 2006-02-14 15:33
★yesjoy★
閱讀(225)
評論(0)
編輯
收藏
所屬分類:
設計模式
新用戶注冊
刷新評論列表
只有注冊用戶
登錄
后才能發表評論。
網站導航:
博客園
IT新聞
Chat2DB
C++博客
博問
管理
相關文章:
單例模式完全剖析(2)---- 探究簡單卻又使人迷惑的單例模式
單例模式完全剖析(1)---- 探究簡單卻又使人迷惑的單例模式
java23種模式一點就通
設計模式的基礎
Copyright ©2025 ★yesjoy★ Powered by:
博客園
模板提供:
滬江博客
主站蜘蛛池模板:
国产成人无码精品久久久免费
|
亚洲国产福利精品一区二区
|
国产亚洲精品资源在线26u
|
在线精品自拍亚洲第一区
|
好爽又高潮了毛片免费下载
|
亚洲av无码专区在线观看下载
|
91麻豆最新在线人成免费观看
|
日韩精品一区二区亚洲AV观看
|
美女被cao网站免费看在线看
|
亚洲精品无码不卡在线播放HE
|
无忧传媒视频免费观看入口
|
国产无遮挡裸体免费视频
|
亚洲欧美日韩中文无线码
|
亚洲偷自拍另类图片二区
|
日韩吃奶摸下AA片免费观看
|
亚洲高清一区二区三区
|
成人a视频片在线观看免费
|
精品国产亚洲AV麻豆
|
亚洲第一永久AV网站久久精品男人的天堂AV
|
亚洲区小说区图片区QVOD
|
免费看成人AA片无码视频吃奶
|
亚洲成色在线综合网站
|
日韩午夜理论免费TV影院
|
亚洲伊人久久大香线蕉影院
|
毛片免费视频观看
|
亚洲乱色伦图片区小说
|
国产精品久免费的黄网站
|
一级成人a免费视频
|
亚洲电影国产一区
|
免费无码肉片在线观看
|
免费国产高清毛不卡片基地
|
久久精品国产亚洲AV不卡
|
4444www免费看
|
爱情岛亚洲论坛在线观看
|
亚洲AV无码乱码在线观看富二代
|
91福利视频免费
|
亚洲人成色77777在线观看
|
亚洲国产成人精品女人久久久
|
成**人免费一级毛片
|
sihu国产精品永久免费
|
亚洲啪啪免费视频
|