單例模式:某個類只能有一個實例,提供一個全局的訪問點。spring
單例模式
●核心做用:保證一個類只有一個實例,而且提供一一個訪問該實例的全局訪問點。
●常見應用場景:
一Windows的Task Manager (任務管理器)就是很典型的單例模式
windows的Recycle Bin (回收站)也是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。
項目中,讀取配置文件的類,通常也只有一 個對象。 沒有必要每次使用配置文件數據,每次new-個對象去讀取。
網站的計數器,通常也是 採用單例模式實現,不然難以同步。
-應用程序的日誌應用 , -般都何用單例模式實現,這通常是因爲共享的日誌文件一直處於打開狀態,由於只能有一個實例去操做
, 不然內容很差追加。
數據庫鏈接池的設計-般也是採用單例模式 ,由於數據庫鏈接是一種數據庫資源。
-操做系統的文件系統 ,也是大的單例模式實現的具體例子,-個操做系統只能有一個文件系統。
Application也是單例的典型應用( Servlet編程中會涉及到)
-在Spring中 ,每一個Bean默認就是單例的,這樣作的優勢是Spring容器能夠管理
-在servlet編程中 ,每一個Servlet也是單例
-在spring MVC框架/struts1框架中,控制器對象也是單例
●單例模式的優勢:
-因爲單例模式只生成一 個實例,減小了系統性能開銷,當-個對象的產生須要
比較多的資源時,如讀取配置、產生其餘依賴對象時 ,則能夠經過在應用啓動
時直接產生一個單例對象,而後永久駐留內存的方式來解決
單例模式能夠在系統設置全局的訪問點,優化環共享資源訪問,例如能夠設計
一個單例類,負責全部數據表的映射處理
●常見的五種單例模式實現方式:
主要:
餓漢式(線程安全,調用效率高。可是,不能延時加載。)
//類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
//方法沒有同步,調用效率高!
public class SingletonDemo1 {
//類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){
}
//方法沒有同步,調用效率高!
public static SingletonDemo1 getInstance(){
return instance;
}
}
懶漢式(線程安全,調用效率不高。可是,能夠延時加載。)
lazy load 延遲加載,懶加載,真正用的時候纔去加載
資源利用率高了,可是每次調用getInstance都要同步,併發效率低了
public class SingletonDemo2 {
//類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
private static SingletonDemo2 instance;
private SingletonDemo2(){ //私有化構造器
}
//方法同步,調用效率低!
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
其餘:
雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用)
靜態內部類式(線程安全,調用效率高。可是,能夠延時加載)
枚舉單例(線程安全,調用效率高,不能延時加載)
1.測試餓漢式單例模式
public class SingletonDemo1 {
//類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){
}
//方法沒有同步,調用效率高!
public static SingletonDemo1 getInstance(){
return instance;
}
}
2.測試懶漢式單例模式
public class SingletonDemo2 {
//類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
private static SingletonDemo2 instance;
private SingletonDemo2(){ //私有化構造器
}
//方法同步,調用效率低!
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
3. 雙重檢查鎖實現單例模式
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
public static SingletonDemo3 getInstance() {
if (instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
private SingletonDemo3() {
}
}
4.測試靜態內部類實現單例模式
* 這種方式:線程安全,調用效率高,而且實現了延時加載!
public class SingletonDemo4 {
private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private SingletonDemo4(){
}
//方法沒有同步,調用效率高!
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
5.測試枚舉式實現單例模式(沒有延時加載)
public enum SingletonDemo5 {
//這個枚舉元素,自己就是單例對象!
INSTANCE;
//添加本身須要的操做!
public void singletonOperation(){
}
}
* 測試懶漢式單例模式(如何防止反射和反序列化漏洞)
public class SingletonDemo6 implements Serializable {
//類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
private static SingletonDemo6 instance;
private SingletonDemo6(){ //私有化構造器
if(instance!=null){
throw new RuntimeException();
}
}
//方法同步,調用效率低!
public static synchronized SingletonDemo6 getInstance(){
if(instance==null){
instance = new SingletonDemo6();
}
return instance;
}
//反序列化時,若是定義了readResolve()則直接返回此方法指定的對象。而不須要單獨再建立新對象!
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
測試反射和反序列化破解單例模式
public class Client2 {
public static void main(String[] args) throws Exception {
SingletonDemo6 s1 = SingletonDemo6.getInstance();
SingletonDemo6 s2 = SingletonDemo6.getInstance();
System.out.println(s1);
System.out.println(s2);
//經過反射的方式直接調用私有構造器
Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.singleton.SingletonDemo6");
Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
SingletonDemo6 s3 = c.newInstance();
SingletonDemo6 s4 = c.newInstance();
System.out.println(s3);
System.out.println(s4);
}
}
//經過反序列化的方式構造多個對象
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletonDemo6 s3 = (SingletonDemo6) ois.readObject();
System.out.println(s3);
}
}
測試枚舉式實現單例模式
public class Client {
public static void main(String[] args) {
SingletonDemo4 s1 = SingletonDemo4.getInstance();
SingletonDemo4 s2 = SingletonDemo4.getInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(SingletonDemo5.INSTANCE==SingletonDemo5.INSTANCE);
}
}
* 測試多線程環境下五種建立單例模式的效率
public class Client3 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
int threadNum = 10;
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
for(int i=0;i<threadNum;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<1000000;i++){
// Object o = SingletonDemo4.getInstance(); //測試靜態內部類
Object o = SingletonDemo5.INSTANCE; //測試枚舉類
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await(); //main線程阻塞,直到計數器變爲0,纔會繼續往下執行!
long end = System.currentTimeMillis();
System.out.println("總耗時:"+(end-start));
}
}
CountDownLatch -同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一 個或多個線程一直等待。 countDown()當前線程調此方法,則計數減一 (建議放在finally裏執行) await(,調用此方法會直阻塞當前線程,直到計時器的值爲0)