勤加練習,必能得心應手!不期速成,日拱一卒?!
態度決定一切 |
Abstract
在開發中,如果某個實例的創建需要消耗很多系統資源,那么我們通常會使用惰性加載機制,也就是說只有當使用到這個實例的時候才會創建這個實例,這個好處在單例模式中得到了廣泛應用。這個機制在single-threaded環境下的實現非常簡單,然而在multi-threaded環境下卻存在隱患。本文重點介紹惰性加載機制以及其在多線程環境下的使用方法。(作者numberzero,參考IBM文章《Double-checked locking and the Singleton pattern》,歡迎轉載與討論)
1 單例模式的惰性加載
通常當我們設計一個單例類的時候,會在類的內部構造這個類(通過構造函數,或者在定義處直接創建),并對外提供一個static getInstance方法提供獲取該單例對象的途徑。例如:
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%20%20%20%20%20%20%0A%7B%20%20%20%20%20%20%0A%20%20%20%20private%20static%20Singleton%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton(){
…
}
public static Singleton getInstance(){
return instance;
}
}
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton(){
…
}
public static Singleton getInstance(){
return instance;
}
}
這樣的代碼缺點是:第一次加載類的時候會連帶著創建Singleton實例,這樣的結果與我們所期望的不同,因為創建實例的時候可能并不是我們需要這個實例的時候。同時如果這個Singleton實例的創建非常消耗系統資源,而應用始終都沒有使用Singleton實例,那么創建Singleton消耗的系統資源就被白白浪費了。
為了避免這種情況,我們通常使用惰性加載的機制,也就是在使用的時候才去創建。以上代碼的惰性加載代碼如下:
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%7B%20%20%20%20%20%20%0A%20%20%20%20private%20static%20Singleton%20instance%20%3D%20null%3B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public class Singleton{
private static Singleton instance = null ;
private Singleton(){
…
}
public static Singleton getInstance(){
if (instance == null )
instance = new Singleton();
return instance;
}
}
public class Singleton{
private static Singleton instance = null;
private Singleton(){
…
}
public static Singleton getInstance(){
if (instance == null)
instance = new Singleton();
return instance;
}
}
這樣,當我們第一次調用Singleton.getInstance()的時候,這個單例才被創建,而以后再次調用的時候僅僅返回這個單例就可以了。
2 惰性加載在多線程中的問題
先將惰性加載的代碼提取出來:
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D%20%20%20%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public static Singleton getInstance(){
if (instance == null )
instance = new Singleton();
return instance;
}
public static Singleton getInstance(){
if (instance == null)
instance = new Singleton();
return instance;
} 這是如果兩個線程A和B同時執行了該方法,然后以如下方式執行:
1. A進入if判斷,此時foo為null,因此進入if內
2. B進入if判斷,此時A還沒有創建foo,因此foo也為null,因此B也進入if內
3. A創建了一個Foo并返回
4. B也創建了一個Foo并返回
此時問題出現了,我們的單例被創建了兩次,而這并不是我們所期望的。
3 各種解決方案及其存在的問題
3.1 使用Class鎖機制
以上問題最直觀的解決辦法就是給getInstance方法加上一個synchronize前綴,這樣每次只允許一個現成調用getInstance方法:
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20synchronized%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public static synchronized Singleton getInstance(){
if (instance == null )
instance = new Singleton();
return instance;
}
public static synchronized Singleton getInstance(){
if (instance == null)
instance = new Singleton();
return instance;
}
這種解決辦法的確可以防止錯誤的出現,但是它卻很影響性能:每次調用getInstance方法的時候都必須獲得Singleton的鎖,而實際上,當單例實例被創建以后,其后的請求沒有必要再使用互斥機制了
3.2 double-checked locking
曾經有人為了解決以上問題,提出了double-checked locking的解決方案
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20if%20(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20synchronized(instance)%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if(instance%20%3D%3D%20null)%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20return%20instance%3B%20%20%20%20%20%20%20%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public static Singleton getInstance(){
if (instance == null )
synchronized (instance){
if (instance == null )
instance = new Singleton();
}
return instance;
}
public static Singleton getInstance(){
if (instance == null)
synchronized(instance){
if(instance == null)
instance = new Singleton();
}
return instance;
} 讓我們來看一下這個代碼是如何工作的:首先當一個線程發出請求后,會先檢查instance是否為null,如果不是則直接返回其內容,這樣避免了進入 synchronized塊所需要花費的資源。其次,即使第2節提到的情況發生了,兩個線程同時進入了第一個if判斷,那么他們也必須按照順序執行 synchronized塊中的代碼,第一個進入代碼塊的線程會創建一個新的Singleton實例,而后續的線程則因為無法通過if判斷,而不會創建多余的實例。
上述描述似乎已經解決了我們面臨的所有問題,但實際上,從JVM的角度講,這些代碼仍然可能發生錯誤。
對于JVM而言,它執行的是一個個Java指令。在Java指令中創建對象和賦值操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM并不保證這兩個操作的先后順序,也就是說有可能JVM會為新的Singleton實例分配空間,然后直接賦值給instance成員,然后再去初始化這個Singleton實例。這樣就使出錯成為了可能,我們仍然以A、B兩個線程為例:
1. A、B線程同時進入了第一個if判斷
2. A首先進入synchronized塊,由于instance為null,所以它執行instance = new Singleton();
3. 由于JVM內部的優化機制,JVM先畫出了一些分配給Singleton實例的空白內存,并賦值給instance成員(注意此時JVM沒有開始初始化這個實例),然后A離開了synchronized塊。
4. B進入synchronized塊,由于instance此時不是null,因此它馬上離開了synchronized塊并將結果返回給調用該方法的程序。
5. 此時B線程打算使用Singleton實例,卻發現它沒有被初始化,于是錯誤發生了。
4 通過內部類實現多線程環境中的單例模式
為了實現慢加載,并且不希望每次調用getInstance時都必須互斥執行,最好并且最方便的解決辦法如下:
Java代碼 < type="application/x-shockwave-flash" width="14" height="15" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src="http://xupo.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20Singleton%7B%20%20%20%20%20%20%0A%20%20%20%20private%20Singleton()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%E2%80%A6%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20private%20static%20class%20SingletonContainer%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20private%20static%20Singleton%20instance%20%3D%20new%20Singleton()%3B%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%20%20%20%20public%20static%20Singleton%20getInstance()%7B%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20return%20SingletonContainer.instance%3B%20%20%20%20%20%20%0A%20%20%20%20%7D%20%20%20%20%20%20%0A%7D%20%20%20%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14">
public class Singleton{
private Singleton(){
…
}
private static class SingletonContainer{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonContainer.instance;
}
}
public class Singleton{
private Singleton(){
…
}
private static class SingletonContainer{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonContainer.instance;
}
}
JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當我們第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,并且會保證把賦值給instance的內存初始化完畢,這樣我們就不用擔心3.2中的問題。此外該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了3.1中的低效問題。最后instance是在第一次加載SingletonContainer類時被創建的,而 SingletonContainer類則在調用getInstance方法的時候才會被加載,因此也實現了惰性加載。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/fancyerII/archive/2010/03/15/5382349.aspx
通過dos窗口 進入到Oracle安裝bin目錄下基本介紹:
showModalDialog() (IE 4+ 支持)
showModelessDialog() (IE 5+ 支持)
window.showModalDialog() 方法用來創建一個顯示HTML內容的模態對話框。
window.showModelessDialog() 方法用來創建一個顯示HTML內容的非模態對話框。
使用方法:
vReturnValue = window.showModalDialog(sURL [, vArguments] [,sFeatures])
vReturnValue = window.showModelessDialog(sURL [, vArguments] [,sFeatures])
參數說明:
sURL -- 必選參數,類型:字符串。用來指定對話框要顯示的文檔的URL。
vArguments -- 可選參數,類型:變體。用來向對話框傳遞參數。傳遞的參數類型不限,包括數組等。對話框通過window.dialogArguments來取得傳遞進來的參數。
sFeatures -- 可選參數,類型:字符串。用來描述對話框的外觀等信息,可以使用以下的一個或幾個,用分號“;”隔開。
----------------
1. dialogHeight: 對話框高度,不小于100px
2. dialogWidth: 對話框寬度。
3. dialogLeft: 離屏幕左的距離。
4. dialogTop: 離屏幕上的距離。
5. center: { yes | no | 1 | 0 } : 是否居中,默認yes,但仍可以指定高度和寬度。
6. help: {yes | no | 1 | 0 }: 是否顯示幫助按鈕,默認yes。
7. resizable: {yes | no | 1 | 0 } [IE5+]: 是否可被改變大小。默認no。
8. status: {yes | no | 1 | 0 } [IE5+]: 是否顯示狀態欄。默認為yes[ Modeless]或no[Modal]。
9. scroll: { yes | no | 1 | 0 | on | off }:是否顯示滾動條。默認為yes。
下面幾個屬性是用在HTA中的,在一般的網頁中一般不使用。
10. dialogHide:{ yes | no | 1 | 0 | on | off }:在打印或者打印預覽時對話框是否隱藏。默認為no。
11. edge:{ sunken | raised }:指明對話框的邊框樣式。默認為raised。
12. unadorned:{ yes | no | 1 | 0 | on | off }:默認為no。
參數傳遞:
1. 要想對話框傳遞參數,是通過vArguments來進行傳遞的。類型不限制,對于字符串類型,最大為4096個字符。也可以傳遞對象,例如:
-------------------------------
parent.htm
<script>
var obj = new Object();
obj.name="51js";
window.showModalDialog("modal.htm",obj,"dialogWidth=200px;dialogHeight=100px");
</script>
modal.htm
<script>
var obj = window.dialogArguments
alert("您傳遞的參數為:" + obj.name)
</script>
-------------------------------
2. 可以通過window.returnValue向打開對話框的窗口返回信息,當然也可以是對象。例如:
------------------------------
parent.htm
<script>
str =window.showModalDialog("modal.htm",,"dialogWidth=200px;dialogHeight=100px");
alert(str);
</script>
modal.htm
<script>
window.returnValue="http://homepage.yesky.com";
對于梅花雪樹選中節點的刪除:級聯刪除該節點下所有子孫節點
本來的目標是 MAX(...) ,結果 這么一句 HQL 語句總是得不到結構:
string query = "SELECT MAX(ID) FROM Student";
另外兩句:"SELECT MAX(Student.ID) FROM Student " 和 "SELECT MAX(ID) FROM Student AS stud";
同樣得不到結果,返回的的均是 Student 對象集合
剛開始以為不支持 MAX 函數,但是SELECT COUNT() FROM Student ,就可以正確返回紀律數,而且很多文檔也多證明了HQL支持ANSI SQL集合函數,
終于發現 只有 "SELECT MAX(stud.ID) FROM Student AS stud";
當然 "SELECT MAX(stud.ID) FROM Student stud" 也是正確的
轉載請標明出處:http://blog.csdn.net/Jinglecat/archive/2005/08/03/445296.aspx
select t1.id,t1.parent_sys_fun_point_id,t1.sys_fun_point_type,t1.sys_fun_point_state,t2.sys_fun_point_ver,/*
* (non-Javadoc)
*
* @see com.cns.certservice.dao.CertBindTabDao#updateCertBind(com.cns.certservice.vo.CertBindView)
*/
public boolean updateCertBind(CertBindView view) throws DAOException {
boolean res = false;
if (view.getId()==0) {
throw new DAOException("getId from CertBindView is null!");
}
Session session= null;
try {
session = HibernateTemplate.getInstance().getSession();
Certbind c = (Certbind) session.get(Certbind.class, view.getId());
if (!CheckEmpty.isEmpty(view.getAgentcode()))
c.setAgentcode(view.getAgentcode());
if (!CheckEmpty.isEmpty(view.getExtend1()))
c.setExtend1(view.getExtend1());
if (!CheckEmpty.isEmpty(view.getExtend2()))
c.setExtend2(view.getExtend2());
if (!CheckEmpty.isEmpty(view.getIpasskeyno()))
c.setIpasskeyno(view.getIpasskeyno());
if (!CheckEmpty.isEmpty(view.getUsercode()))
c.setUsercode(view.getUsercode());
if (view.getAgenttype() != 0)
c.setAgenttype(view.getAgenttype());
res = HibernateTemplate.getInstance().updateObject(c);
} catch (HibernateException e) {
log.error(e,e);
throw new DAOException(e);
}finally{
if (session != null && session.isConnected())
session.close();
}
return res;
}
private Certbind getCertBind(CertBindView view) {
Certbind c = new Certbind();
if (view.getAddtime() != null)
c.setAddtime(view.getAddtime());
if (!CheckEmpty.isEmpty(view.getAgentcode()))
c.setAgentcode(view.getAgentcode());
if (!CheckEmpty.isEmpty(view.getExtend1()))
c.setExtend1(view.getExtend1());
if (!CheckEmpty.isEmpty(view.getExtend2()))
c.setExtend2(view.getExtend2());
if (!CheckEmpty.isEmpty(view.getIpasskeyno()))
c.setIpasskeyno(view.getIpasskeyno());
if (!CheckEmpty.isEmpty(view.getUsercode()))
c.setUsercode(view.getUsercode());
if(view.getId()!=0)c.setId(view.getId());
if (view.getAgenttype() != 0)
c.setAgenttype(view.getAgenttype());
return c;
}
private CertBindView getCertBindView(Certbind view) {
CertBindView c = new CertBindView();
if (view.getAddtime() != null)
c.setAddtime(view.getAddtime());
if (!CheckEmpty.isEmpty(view.getAgentcode()))
c.setAgentcode(view.getAgentcode());
if (!CheckEmpty.isEmpty(view.getExtend1()))
c.setExtend1(view.getExtend1());
if (!CheckEmpty.isEmpty(view.getExtend2()))
c.setExtend2(view.getExtend2());
if (!CheckEmpty.isEmpty(view.getIpasskeyno()))
c.setIpasskeyno(view.getIpasskeyno());
if (!CheckEmpty.isEmpty(view.getUsercode()))
c.setUsercode(view.getUsercode());
if(view.getId()!=0)c.setId(view.getId());
if (view.getAgenttype() != 0)
c.setAgenttype(view.getAgenttype());
return c;
}
| |||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
---|---|---|---|---|---|---|---|---|---|
25 | 26 | 27 | 28 | 29 | 30 | 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 |