文章優先發布在個人博客。 https://www.xdx97.com/article/699258099130695680
所謂單例模式,就是對某個類只能存在一個對象實例,並且該類只提供一個獲取其對象實例的方法(靜態方法)
單例模式一共有八種方式
1、餓漢式 (靜態常量)
2、餓漢式 (靜態代碼塊)
3、懶漢式 (靜態方法內部判斷,線程不安全)
4、懶漢式 (線程安全,同步方法)
5、懶漢式 (線程不安全,同步代碼塊)
6、雙重檢查
7、靜態內部類
8、枚舉
1、餓漢式
1-1:餓漢式(靜態常量)
1、構建
1、構造器私有化(防止 new)
2、類的內部創建對象
3、向外暴露一個靜態的公共方法(getInstance)
2、代碼演示
public class Main {
// 測試
public static void main(String[] args) {
Singleton one = Singleton.getInstance();
Singleton two = Singleton.getInstance();
System.out.println(one == two); // true
System.out.println("one.hashCode() = " + one.hashCode()); // 兩個hashcode相同
System.out.println("two.hashCode() = " + two.hashCode());
}
}
class Singleton {
// 1、私有化,防止 new
private Singleton(){}
// 2、本類內部創建實例
private final static Singleton instance = new Singleton();
// 3、提供一個公有的靜態方法,返回實例對象
public static Singleton getInstance(){
return instance;
}
}
3、分析
3-1:優缺點
- 優點:寫法比較簡單,就是在類加載的時候完成實例化。避免了線程同步問題。
- 缺點:在類加載的時候就完成實例化,沒有達到懶加載的效果。如果從始至終從未使用過這個實例,則會造成內存浪費。
1-2:餓漢式(靜態代碼塊)
1、代碼演示 (測試和上面的測試一樣,就不贅述了)
class Singleton {
// 1、私有化,防止 new
private Singleton(){}
// 2、創建變量
private static Singleton instance;
// 3、靜態創建對象
static {
instance = new Singleton();
}
// 4、提供一個公有的靜態方法,返回實例對象
public static Singleton getInstance(){
return instance;
}
}
2、優缺點,和上面一樣不再贅述。
2、懶漢式
1、懶漢式(線程不安全的)
1、代碼演示
class Singleton {
// 1、私有化,防止 new
private Singleton(){}
// 2、創建變量
private static Singleton instance;
// 3、提供一個公有的靜態方法,返回實例對象
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
2、優缺點
- 優點:起到了懶加載的作用
- 缺點:線程不安全。(一個線程進入 if 語句後,還沒來得及往下執行,另一個線程也通過了判斷語句,這時候就會產生多個實例 )
2、懶漢式(使用 synchronized 同步方法, 保證線程安全)
1、代碼演示
class Singleton {
// 1、私有化,防止 new
private Singleton(){}
// 2、創建變量
private static Singleton instance;
// 3、提供一個同步靜態方法,返回實例對象。解決線程安全問題
public synchronized static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
2、優缺點
- 優點:解決了線程安全的問題。
- 缺點:方法使用了同步,效率太低了。
3、懶漢式 (線程不安全,同步代碼塊)
不可以這樣寫,它連最起碼的線程安全都無法保證。
1、代碼演示
class Singleton {
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
if (instance == null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
3、雙重檢查
1、代碼演示
class Singleton {
private Singleton(){}
private volatile static Singleton instance;
public static Singleton getInstance(){
if (instance == null){
// 1
synchronized(Singleton.class){
// 2
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
說明:volatile可以保證可見性、有序性,還有禁止指令重排序。比如當我們線程A已經執行到了 2,線程B執行到了 1。這個時候線程A執行結束創建了對象,B再進來了發現對象已經創建了(volatile的作用)就不再創建了。而之後的線程直接在第一個if就被擋住了。
2、優缺點
- 優點:做到了懶加載、解決了線程安全問題和效率問題。
7、靜態內部類
1、代碼演示
class Singleton {
private Singleton(){}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
說明:
- 外部類裝載的時候,內部類不會進行裝載的。
- 當我們調用getInstance的時候內部類會進行裝載,裝載的過程是線程安全的。
2、優缺點
- 優點:避免線程安全問題,滿足了延遲加載,效率高。
8、枚舉
public class Main {
public static void main(String[] args) {
Singleton one = Singleton.INSTANCE;
Singleton two = Singleton.INSTANCE;
System.out.println(one == two);
System.out.println(one.hashCode());
System.out.println(two.hashCode());
}
}
enum Singleton {
INSTANCE;
public void fun(){
System.out.println("fun...");
}
}
2、優缺點
- 優點:避免線程安全問題,而且還能防止反序列化重新創建新的對象。
9、其它
1、JDK裏面那裏使用到了單例模式?
Runtime裏面就用到了單例模式的餓漢模式,它把構造方法私有化了,然後直接使用靜態常量創建對象。
2、單例模式注意事項
- 單例保證了系統內存中該類只存在一個對象,節省了系統資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統的性能。
- 當想實例化一個單例類的時候,必須記住使用相應的獲取對象的方法,而不是使用new
- 單例模式使用場景:需要頻繁的進行創建和銷燬的對象、創建對象時消耗過多或消費資源過多(重量級對象),但又經常使用到的對象、工具類對象、頻繁訪問數據庫或文件的對象(比如數據源、session工程等)
如果對你有幫助了的話,可以關注我的公衆號,一起成長噢