一、概述 單例模式確保一個類只有一個實例,並提供一個安全的訪問點。 二、解決問題 從概述中我們知道,單例模式就是保證系統的一個類只有一個實例。它的作用就是控制受限資源的訪問,確保任何時刻都只有一個線程在訪問一個受保護的資源。或者確保行為和狀態的一致性,避免異常行為。在java web的程式中可能用到 ...
一、概述
單例模式確保一個類只有一個實例,並提供一個安全的訪問點。
二、解決問題
從概述中我們知道,單例模式就是保證系統的一個類只有一個實例。它的作用就是控制受限資源的訪問,確保任何時刻都只有一個線程在訪問一個受保護的資源。或者確保行為和狀態的一致性,避免異常行為。在java web的程式中可能用到最多單例的地方就是jdbc的線程池。
三、結構類圖
四、成員角色
實例變數(uniqueInstance):持有唯一的單例實例,靜態私有訪問許可權,只有本類才能訪問該實例變數。
全局訪問方法(getInstance):提供全局的對單例實例的訪問,任何時候都返回同一個單例實例(uniqueIntance),公共靜態訪問許可權,任何對象都可以訪問到。
五、應用實例
單例模式有三種實現方案,我們來一起來看看如何實現。
1.同步getInstance()方法
package singleton.pattern;
public class SynchronizedSingleton {
//唯一的實例,只有本類才可以訪問
private static SynchronizedSingleton uniqueInstance;
//只有本類才可以實例自己
private SynchronizedSingleton(){
}
//提供對實例的全局訪問點(同步方法,任何時候都只有一個線程可以訪問該方法)
public synchronized static SynchronizedSingleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new SynchronizedSingleton();
}
return uniqueInstance;
}
}
2.“急切”創建實例
package singleton.pattern; public class HurrySingleton { //唯一的一個類實例,用static把它變為靜態的,在靜態初始化器中創建單例 private static HurrySingleton uniqueInstance = new HurrySingleton(); private HurrySingleton(){ } //已經有實例了直接返回 public static HurrySingleton getInstance(){ return uniqueInstance; } }
3.用“雙重檢查加鎖”,減少同步
package singleton.pattern; public class DoubleCheckSingleton { private volatile static DoubleCheckSingleton uniqueInstance; private DoubleCheckSingleton(){ } public static DoubleCheckSingleton getInstance(){ //線程只有第一次進入該方法時才會執行這個if代碼 if(uniqueInstance == null){ //第一個線程獲得類對象鎖,其他線程就不能通過getInstance()方法獲得實例了, synchronized(DoubleCheckSingleton.class){ //這if判斷是為了給第二個或者之後的線程獲得類對象鎖後使用,如果沒有這個if判斷,將會創建多個實例。 //另外uniqueInstance變數必須要用volatile修飾,確保第一個線程實例化了對象後,其他線程能夠立刻可見 //volatile的詳解請參考:http://www.cnblogs.com/dolphin0520/p/3920373.html(Java併發編程:volatile關鍵字解析) if(uniqueInstance == null){ uniqueInstance = new DoubleCheckSingleton(); } } } return uniqueInstance; } }
單例模式在jdbc中的應用,下麵採用雙重檢查加鎖實現資料庫連接池的單例
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
public class ConnectionMgr {
private volatile static BasicDataSource dataSource = null;
private ConnectionMgr() {
}
private static void init() {
if (dataSource != null) {
try {
dataSource.close();
} catch (Exception e) {
//
}
dataSource = null;
}
try {
Properties p = new Properties();
p.setProperty("driverClassName", MySys.getDriver());
p.setProperty("url", MySys.getDatabaseUrl());
p.setProperty("password", MySys.getDatabasePassword());
p.setProperty("username", MySys.getDatabaseUser());
System.out.print("OMS DB URL = "+MySys.getDatabaseUrl() + "\n");
p.setProperty("maxActive", "30"); //最大連接數量
p.setProperty("maxIdle", "10"); //最大空閑連接
p.setProperty("maxWait", "1000"); //超時等待時間以毫秒為單位 1000等於60秒
p.setProperty("removeAbandoned", "false"); //是否自動回收超時連接
p.setProperty("removeAbandonedTimeout", "120"); //超時時間(以秒數為單位)
p.setProperty("testOnBorrow", "true"); //指明是否在從池中取出連接前進行檢驗
p.setProperty("testOnReturn", "false"); //指明是否在歸還到池中前進行檢驗
p.setProperty("testWhileIdle", "false");//指明連接是否被空閑連接回收器(如果有)進行檢驗
p.setProperty("logAbandoned", "true"); //連接被泄露時是否列印
p.setProperty("validationQuery", MySys.getTestQuery()); //
dataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
//
}
}
public static Connection getConnection() throws SQLException {
Connection conn = null;
//雙重檢查加鎖
if (dataSource == null) {
synchronized(BasicDataSource.class){
if (dataSource == null) {
init();
}
}
}
if(dataSource != null){
conn = dataSource.getConnection();
}
return conn;
}
六、優缺點
1、同步getInstance()方法:最安全的單例,但是每次訪問單例實例都要加鎖,增加了性能的開銷。如果系統對性能要求不高可以用。
2、急切實例化:解決了“延遲實例化”帶來的訪問延遲問題,但會影響系統的啟動負擔,而如果該實例一開始創建了卻一直沒被用到會造成資源的浪費。系統啟動和運行負擔小可以用。
3、雙重檢查加鎖:減少了同步的使用,降低了jdk同步的開銷;但是低版本的JDK不能使用,必須是1.5版本或者以上版本的JDK才能使用。系統對性能要求高時使用。
七、應用場景
系統需要保證唯一的實例時使用,例如資料庫連接池和線程池。
八、總結
1.單例模式確保一個類中最多只有一個實例,並且提供訪問這個實例的全局訪問點。
2.實現單例模式需要私有的構造器,一個私有靜態變數和一個能夠被公共訪問的靜態方法。
3.如果使用多個類載入器,要小心單例失效而產生多個實例。