單例模式
保證一個類僅有一個實例(sInstance), 並提供一個訪問該實例的全局訪問點(getInstance).
這就意味着單例通常有如下兩個特點:
- 構造函數是私有的(避免別的地方創建它)
- 有一個static的方法來對外提供一個該單例的實例.
優點:
- 在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)。
- 避免對資源的多重佔用(比如寫文件操作)。
缺點:
- 沒有接口,不能繼承
使用場景:
- 要求生產唯一序列號。
- WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
- 創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。
1 餓漢式單利
餓漢式單例
顧名思義, 就是很餓, 不管三七二十一先創建了一個實例放着, 而不管最終用不用.
缺點:
- 在不需要的情況下就隨便生成對象,消耗內存,不可取
2 懶漢式單例
"懶", 也就是現在懶得創建, 等有用戶要用的時候才創建.
3 線程安全的懶漢式單例
利用synchronized關鍵字來修飾對外提供該類唯一實例的接口(getInstance)來確保在一個線程調用該接口時能阻塞(block)另一個線程的調用, 從而達到多線程安全, 避免重複創建單例.
缺點:
- synchronized有很大的性能開銷. 而且在這裏我們是修飾了getInstance方法, 意味着, 如果getInstance被很多線程頻繁調用時, 每次都會做同步檢查, 會導致程序性能下降.實際上我們要的是單例, 當單例已經存在的時候, 我們是不需要用同步方法來控制的
4 雙重檢查單例模式
這種方式的同步使用會減少同步鎖的佔用比例
缺點
- 容易出現理解錯誤 不加volatile 關鍵字引發不必要的錯誤
5 靜態內部類單例
這種方式, 通過JVM的類加載方式(虛擬機會保證一個類的初始化在多線程環境中被正確的加鎖、同步), 來保證了多線程併發訪問的正確性.
另外, 由於靜態內部類的加載特性 --- 在使用時才加載, 這種方式也達成了懶加載的目的.
缺點
- 完美 ,依賴於特定的語言
代碼實例
package com.jack.idea.test.designmode;
/**
* 單例模式
*
* @author ljs.song
* @date 2017-12-28 15:24
*/
public class Singleton {
public static void main(String[] args) {
XiaoLong xiaoLong = new XiaoLong();
System.out.println(xiaoLong.share("小龍龍布蘭妮四點分享英雄聯盟王者之路和絕地求生吃雞之祕"));
LeiLei leiLei = new LeiLei();
System.out.println(leiLei.share("磊磊四點分享英雄聯盟王者之路和絕地求生吃雞之祕"));
}
}
/**
* 小龍龍布蘭妮分享
*/
class XiaoLong{
public Share share(String title){
Share share = SimpleSingleton.getInstance();
share.title(title);
return share;
}
}
/**
* 磊磊分享
*/
class LeiLei{
public Share share(String title){
Share share = SimpleSingleton.getInstance();
share.title(title);
return share;
}
}
/**
* 分享整理文件
*/
class Share{
private String title;
public void title(String title){
this.title = title;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Share{");
sb.append("fileName : " + this.hashCode()).append(" ,");
sb.append("title='").append(title).append('\'');
sb.append('}');
return sb.toString();
}
}
/**
* 餓漢式
*/
class SimpleSingleton extends Share{
private static SimpleSingleton simpleSingleton = new SimpleSingleton();
private SimpleSingleton(){}
public static SimpleSingleton getInstance(){
return simpleSingleton;
}
}
/**
* 懶漢式(飽漢)
*/
class LazySingleton extends Share{
private static LazySingleton lazySingleton;
private LazySingleton(){}
public static LazySingleton getInstance(){
//在有人第一次分享後才創建分享的整理文件
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
//線程安全的懶漢單例 synchronized
class SynchronizedLazySingleton extends Share{
private static SynchronizedLazySingleton synchronizedLazySingleton;
private SynchronizedLazySingleton(){}
//粗魯人也,直接鎖定整個方法
public static synchronized SynchronizedLazySingleton getInstance(){
//在有人第一次分享後才創建分享的整理文件
if(synchronizedLazySingleton == null){
synchronizedLazySingleton = new SynchronizedLazySingleton();
}
return synchronizedLazySingleton;
}
}
/**
* 雙重檢查鎖定單例
*/
class DCLLazySingleton extends Share{
/**
* 這裏添加volatile 關鍵字是因爲下面註釋 問題根源 S處對象的創建
* 1.分配內存 2.初始化對象 3. 引用指向分配的地址
* 可能會出現指令重排序
* 假如 順序變爲 1,3,2 就有可能導致線程A走進S處發生重排序
* 線程B 在代碼N處發現對象不爲空,但是卻還沒初始化的尷尬
*/
private static volatile DCLLazySingleton dclLazySingleton;
private DCLLazySingleton(){}
public static DCLLazySingleton getInstance(){
//第一次檢查 N
if(dclLazySingleton == null){
synchronized (DCLLazySingleton.class){
//第二次檢查
if(dclLazySingleton == null) {
// 問題根源 S
dclLazySingleton = new DCLLazySingleton();
}
}
}
return dclLazySingleton;
}
}
/**
* 靜態內部類單例
*/
class InnerClassSingleton{
private InnerClassSingleton(){}
public static InnerClassSingleton getInstance(){
return Inner.INNER_CLASS_SINGLETON;
}
static class Inner{
private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
}
}