# 單例模式
單例模式有非常多種的實現方式,具體如下:
## 餓漢式 - 靜態常量
~~~
?package cn.net.smrobot.singleton.type1;
??
?/**
? * 餓漢式,使用靜態變量的方式
? */
?public class Singleton {
??
? ? ?/**
? ? ? * 將構造器私有化
? ? ? */
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 創建本地變量實例
? ? ? */
? ? ?private final static Singleton instance = new Singleton();
??
? ? ?/**
? ? ? * 靜態方法用來返回實例
? ? ? * @return
? ? ? */
? ? ?public static Singleton getInstance() {
? ? ? ? ?return instance;
? ? }
??
??
?}
~~~
優點:
1. 無多線程問題,隨著類的加載被加載
缺點:
1. 沒有達到懶加載的效果,如果一直沒有使用到這個實例,可能會導致內存的浪費。
如果確定該實例一定會被使用,則可以使用這個方式。
## 餓漢式 - 靜態代碼塊
~~~
?package cn.net.smrobot.singleton.type2;
??
?/**
? * 餓漢式 - 使用靜態代碼塊
? */
?public class Singleton {
??
? ? ?public static void main(String[] args) {
??
? ? }
??
? ? ?/**
? ? ? * 私有化構造方法
? ? ? */
? ? ?private Singleton() {}
??
? ? ?private static Singleton instance;
??
? ? ?/**
? ? ? * 靜態代碼塊實例對象
? ? ? */
? ? ?static {
? ? ? ? ?instance = new Singleton();
? ? }
??
? ? ?public static Singleton getInstance() {
? ? ? ? ?return instance;
? ? }
??
?}
~~~
效果與靜態常量的方法是一樣的。
## 懶漢式 - 線程不安全
~~~
?package cn.net.smrobot.singleton.type3;
??
?/**
? * 懶漢式
? * 線程不安全
? */
?public class Singleton {
? ? ?public static void main(String[] args) {
? ? ? ? ?Singleton instance = Singleton.getInstance();
? ? ? ? ?Singleton instance1 = Singleton.getInstance();
? ? ? ? ?System.out.println(instance == instance1);
? ? ? ? ?System.out.println("hashCode = " + instance.hashCode());
? ? ? ? ?System.out.println("hashCode = " + instance1.hashCode());
? ? }
??
? ? ?private static Singleton instance;
??
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 調用該方法的使用才去創建單例對象
? ? ? */
? ? ?public static Singleton getInstance() {
? ? ? ? ?if (instance == null) {
? ? ? ? ? ? ?instance = new Singleton();
? ? ? ? }
? ? ? ? ?return instance;
? ? }
??
??
?}
~~~
優點:
1. 可以起到懶加載的效果
缺點:
1. 在多線程的環境下,如果一個線程進入`if(instance == null)`語句判斷,但是還沒來得及執行`new instance()`,別的線程也進入了這個語句判斷,這樣可能會導致多個實例對象被創建,達不到單例的效果。
> 在實際開發中不要使用這種方式
## 懶漢式 - 線程安全
~~~
?package cn.net.smrobot.singleton.type4;
??
?/**
? * 懶漢式
? * 線程安全 - 使用同步方法
? */
?public class Singleton {
? ? ?public static void main(String[] args) {
? ? ? ? ?Singleton instance = Singleton.getInstance();
? ? ? ? ?Singleton instance1 = Singleton.getInstance();
? ? ? ? ?System.out.println(instance == instance1);
? ? ? ? ?System.out.println("hashCode = " + instance.hashCode());
? ? ? ? ?System.out.println("hashCode = " + instance1.hashCode());
? ? }
??
? ? ?private static Singleton instance;
??
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 調用該方法的使用才去創建單例對象
? ? ? * 加入同步處理代碼
? ? ? * 解決線程安全問題
? ? ? */
? ? ?public static synchronized Singleton getInstance() {
? ? ? ? ?if (instance == null) {
? ? ? ? ? ? ?instance = new Singleton();
? ? ? ? }
? ? ? ? ?return instance;
? ? }
??
??
?}
~~~
這種方式使用`synchronized`同步的方式,但是在方法級別進行同步效率比較低,在實際開發中也不推薦使用。
## 懶漢式 - 同步代碼塊
~~~
?package cn.net.smrobot.singleton.type5;
??
?/**
? * 懶漢式
? * 線程安全 - 使用同步代碼塊
? */
?public class Singleton {
? ? ?public static void main(String[] args) {
? ? ? ? ?Singleton instance = Singleton.getInstance();
? ? ? ? ?Singleton instance1 = Singleton.getInstance();
? ? ? ? ?System.out.println(instance == instance1);
? ? ? ? ?System.out.println("hashCode = " + instance.hashCode());
? ? ? ? ?System.out.println("hashCode = " + instance1.hashCode());
? ? }
??
? ? ?private static Singleton instance;
??
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 調用該方法的使用才去創建單例對象
? ? ? * 加入同步處理代碼
? ? ? * 解決線程安全問題
? ? ? */
? ? ?public static Singleton getInstance() {
? ? ? ? ?if (instance == null) {
? ? ? ? ? ? ?synchronized (Singleton.class) {
? ? ? ? ? ? ? ? ?instance = new Singleton();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ?return instance;
? ? }
??
??
?}
~~~
仍然是線程不安全的,例如在進行if判斷時候有多個線程進入該分支,就會導致對象被同步的創建多次。
**不要使用**
## 雙重檢查 - Double Check
推薦使用,可以解決線程安全,懶加載,效率問題。
~~~
?package cn.net.smrobot.singleton.type6;
??
?/**
? * 雙重檢查
? */
?public class Singleton {
? ? ?public static void main(String[] args) {
? ? ? ? ?Singleton instance = Singleton.getInstance();
? ? ? ? ?Singleton instance1 = Singleton.getInstance();
? ? ? ? ?System.out.println(instance == instance1);
? ? ? ? ?System.out.println("hashCode = " + instance.hashCode());
? ? ? ? ?System.out.println("hashCode = " + instance1.hashCode());
? ? }
??
? ? ?/**
? ? ? * volatile 關鍵字很重要
? ? ? */
? ? ?private static volatile Singleton instance;
??
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 調用該方法的使用才去創建單例對象
? ? ? * 使用兩個if進行雙重檢查
? ? ? */
? ? ?public static Singleton getInstance() {
? ? ? ? ?if (instance == null) {
? ? ? ? ? ? ?synchronized (Singleton.class) {
? ? ? ? ? ? ? ? ?if (instance == null) {
? ? ? ? ? ? ? ? ? ? ?// 再進行一步判斷
? ? ? ? ? ? ? ? ? ? ?instance = new Singleton();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ?return instance;
? ? }
?}
~~~
## 靜態內部類
~~~
?package cn.net.smrobot.singleton.type7;
??
?/**
? * 靜態內部類
? */
?public class Singleton {
? ? ?public static void main(String[] args) {
??
? ? }
??
? ? ?private Singleton() {}
??
? ? ?/**
? ? ? * 只有調用getInstance方法時靜態內部類才會被加載
? ? ? * 實例對象會被唯一創建一次
? ? ? * 利用JVM底層機制類裝載的時候是線程安全的
? ? ? */
? ? ?private static class SingletonInstance {
? ? ? ? ?private final static Singleton INSTANCE = new Singleton();
? ? }
??
? ? ?public static Singleton getInstance() {
? ? ? ? ?return SingletonInstance.INSTANCE;
? ? }
?}
~~~
可以達到線程安全和延遲加載的效果,線程安全是利用JVM的類裝載機制,JVM可以保證在加載一個類的時候是線程安全的,推薦使用。
## 枚舉類型
~~~
?package cn.net.smrobot.singleton.type8;
??
?/**
? * 枚舉模式
? */
?public enum Singleton {
??
? ? ?INSTANCE;
? ? ?
?}
~~~
由于其他單例模式的實質都是將構造方法私有化,但是通過反射是可以將構造方法設置為共有化的,于是就可以創建更多的對象了。而反射的代碼會判斷該類型是不是枚舉類型,如果是枚舉類型則會直接拋出異常。同時枚舉類型的序列化與反序列化可以保證對象一致。
可以避免多線程同步問題并且能夠防止反序列化重新創建新的對象,推薦使用。
> 參考:https://www.cnblogs.com/chiclee/p/9097772.html
## JDK中的源碼
RunTime
## 單例模式使用場景
1. 需要頻繁的進行創建和銷毀的對象。
2. 創建對象時耗時過多或者資源浪費過多。
3. 經常用到的對象,工具類對象,頻繁訪問數據庫或文件的對象(數據源,session工廠)。
4. Spring IOC容器的ApplicationContext用的是餓漢式的單例模式。
- 第一章 Java基礎
- ThreadLocal
- Java異常體系
- Java集合框架
- List接口及其實現類
- Queue接口及其實現類
- Set接口及其實現類
- Map接口及其實現類
- JDK1.8新特性
- Lambda表達式
- 常用函數式接口
- stream流
- 面試
- 第二章 Java虛擬機
- 第一節、運行時數據區
- 第二節、垃圾回收
- 第三節、類加載機制
- 第四節、類文件與字節碼指令
- 第五節、語法糖
- 第六節、運行期優化
- 面試常見問題
- 第三章 并發編程
- 第一節、Java中的線程
- 第二節、Java中的鎖
- 第三節、線程池
- 第四節、并發工具類
- AQS
- 第四章 網絡編程
- WebSocket協議
- Netty
- Netty入門
- Netty-自定義協議
- 面試題
- IO
- 網絡IO模型
- 第五章 操作系統
- IO
- 文件系統的相關概念
- Java幾種文件讀寫方式性能對比
- Socket
- 內存管理
- 進程、線程、協程
- IO模型的演化過程
- 第六章 計算機網絡
- 第七章 消息隊列
- RabbitMQ
- 第八章 開發框架
- Spring
- Spring事務
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 數據庫
- Mysql
- Mysql中的索引
- Mysql中的鎖
- 面試常見問題
- Mysql中的日志
- InnoDB存儲引擎
- 事務
- Redis
- redis的數據類型
- redis數據結構
- Redis主從復制
- 哨兵模式
- 面試題
- Spring Boot整合Lettuce+Redisson實現布隆過濾器
- 集群
- Redis網絡IO模型
- 第十章 設計模式
- 設計模式-七大原則
- 設計模式-單例模式
- 設計模式-備忘錄模式
- 設計模式-原型模式
- 設計模式-責任鏈模式
- 設計模式-過濾模式
- 設計模式-觀察者模式
- 設計模式-工廠方法模式
- 設計模式-抽象工廠模式
- 設計模式-代理模式
- 第十一章 后端開發常用工具、庫
- Docker
- Docker安裝Mysql
- 第十二章 中間件
- ZooKeeper