原始碼在我的github和gitee中獲取
目錄
java23種設計模式—— 一、設計模式介紹
java23種設計模式—— 二、單例模式
java23種設計模式——三、工廠模式
java23種設計模式——四、原型模式
java23種設計模式——五、建造者模式
java23種設計模式——六、配接器模式
java23種設計模式——七、橋接模式
java23種設計模式——八、組合模式
介紹
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一,這種型別的設計模式屬于創建型模式,它提供了一種創建物件的最佳方式,
這種模式涉及到一個單一的類,該類負責創建自己的物件,同時確保只有單個物件被創建,這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要實體化該類的物件,
注意:
- 1、單例類只能有一個實體,
- 2、單例類必須自己創建自己的唯一實體,
- 3、單例類必須給所有其他物件提供這一實體,
實作方式
餓漢式單例(靜態常量,執行緒安全)
顧名思義,餓漢式單例它很“餓”,所以一開始就創建了唯一但單例實體,但如果你沒有使用過這個實體,就會造成記憶體的浪費
/**
* 餓漢式單例
* 優點:簡單,在類裝載時就完成了實體化,避免了執行緒同步問題,執行緒安全
* 缺點:由于這個類已經完成了實體化,如果從始至終都沒有用過這個實體,就會造成記憶體的浪費
*/
public class SingletonTest01 {
public static void main(String[] args) {
Signleton instance1= Signleton.getInstance();
Signleton instance2 = Signleton.getInstance();
System.out.println(instance1==instance2);
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
class Signleton{
//1、構造器私有化,外部無法通過new新建
private Signleton(){ }
//2、內部創建物件實體
private final static Signleton instance = new Signleton();
//3、提供一個公有的靜態方法,回傳實體物件
public final static Signleton getInstance(){
return instance;
}
}
輸出結果
true
1163157884
1163157884
可以看到輸出的是同一個實體
餓漢式單例(靜態代碼塊,執行緒安全)
和之前的方式類似,只不過將類實體化的程序放在了靜態代碼塊中,也就是類裝載的時候,
就執行靜態代碼塊中的代碼,優缺點和之前一樣
/**
* 和之前的方式類似,只不過將類實體化的程序放在了靜態代碼塊中,也就是類裝載的時候,
* 就執行靜態代碼塊中的代碼,優缺點和之前一樣
*/
public class SingletonTest02 extends Thread{
public static void main(String[] args) {
Signleton instance1= Signleton.getInstance();
Signleton instance2 = Signleton.getInstance();
System.out.println(instance1==instance2);
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
class Signleton{
//1、構造器私有化,外部無法通過new新建
private Signleton(){}
//2、內部創建物件實體
private static Signleton instance;
static {//靜態代碼塊種,創建單例物件
instance = new Signleton();
}
//3、提供一個公有的靜態方法,回傳實體物件
public final static Signleton getInstance(){
return instance;
}
}
輸出
true
1163157884
1163157884
懶漢式(執行緒不安全)
同樣,顧名思義,懶漢式單例它很懶,只有在你用到它時,它才會創建一個實體,
/**
* 餓漢式-執行緒不安全
* 優點:起到了懶加載的效果,但是只能在單執行緒下使用
* 如果在多執行緒下,如果一個執行緒進入了if判斷陳述句塊,
* 還沒來得及向下執行,另一個執行緒也進入這個判斷陳述句,就會產生多個實體(違背單例模式),
* 實際開發中,不要使用這種方式
*/
public class SingletonTest03 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static Signleton instance;
private Signleton(){}
//提供一個靜態的公有方法,當呼叫方法時,才去創建instance
public static Signleton getInstance(){
if(instance == null){//如果為空再去創建物件
instance = new Signleton();
}
return instance;
}
}
輸出
546405844
135417039
135417039
802181073
135417039
135417039
135417039
802181073
135417039
135417039
這里我選了個比較極端的情況,如果你的電腦配置比較好,可能運行幾次結果都是符合單例模式的,
懶漢式(同步方法,執行緒安全)
上面方法之所以會存在執行緒不安全的情況,是因為多執行緒情況下,可能會有多條執行緒同時判斷單例是否創建,那么要解決這個問題 ,只需要同步getInstance()方法
/**
* 解決了執行緒不安全的問題
* 但是大大降低了效率 每個執行緒想獲得實體的時候,執行getInstance()方法都要進行同步
*/
public class SingletonTest04 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static Signleton instance;
private Signleton(){}
//提供一個靜態的公有方法,當呼叫方法時,才去創建instance
public static synchronized Signleton getInstance(){
if(instance == null){//如果為空再去創建物件
instance = new Signleton();
}
return instance;
}
}
結果
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
但是,synchronized是一個很重量的同步鎖,而我們每次執行getInstance()時都會進行同步,極其影響效率
懶漢式(雙重檢查,執行緒安全)
雙檢鎖,又叫雙重校驗鎖,綜合了懶漢式和餓漢式兩者的優缺點整合而成,看上面代碼實作中,特點是在synchronized關鍵字內外都加了一層 if 條件判斷,這樣既保證了執行緒安全,又比直接上鎖提高了執行效率,還節省了記憶體空間
/**
* 懶漢模式-雙重檢查
* 進行了兩次if判斷檢查,這樣就保證執行緒安全了
* 通過判斷是否為空,來確定是否 需要再次實體化
*/
public class SingletonTest05 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
private static volatile Signleton instance;//volatile保證可見性
private Signleton(){}
//提供一個靜態的公有方法,加入雙重檢查代碼,解決執行緒安全問題,同時解決懶加載問題
public static Signleton getInstance() {
if (instance == null) {
synchronized (Signleton.class) {
if (instance == null) {
instance = new Signleton();
}
}
}
return instance;
}
}
運行結果
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
推薦使用
靜態內部類(執行緒安全)
/**
* 靜態內部類實作單例模式
* 該方法采用了類裝載機制來保證初始化實體時只有一個執行緒
* 靜態內部類在Signleton類被裝載時并不會立即實體化,而是需要實體化時,才會裝載SignletonInstance類
* 類的靜態屬性只會在第一次加載類的時候初始化
* 避免了執行緒不安全,利用靜態內部類實作懶加載,效率高
*/
public class SingletonTest07 {
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
}
}
}
class Signleton{
//構造器私有
private Signleton(){}
//靜態內部類,該類中有一個靜態屬性Signleton
private static class SignletonInstance{
private static final Signleton instance = new Signleton();
}
//提供一個靜態的公有方法,直接回傳SignletonInstance.instance
public static Signleton getInstance() {
return SignletonInstance.instance;
}
}
結果
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
這種方式較為簡單,推薦使用
列舉(執行緒安全)
/**
* @author codermy
* @createTime 2020/5/14
* 列舉方法實作單例模式
* 借助jdk1.5中添加的列舉類來實作單例模式,
* 不僅能避免多執行緒同步問題,而且還能防止反序列化重新創建新物件
*/
public class SingletonTest08 {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
singleton.Ok();
for (int i = 0; i <10 ; i++) {
new Thread(() -> System.out.println(Singleton.INSTANCE.hashCode()) ).start();
}
}
}
enum Singleton{
INSTANCE;//屬性
public void Ok(){
System.out.println("ok");
}
}
結果
ok
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
可以看出,列舉實作單例模式,最為簡潔,較為推薦,但是正是因為它簡潔,導致可讀性較差
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/47216.html
標籤:Java
上一篇:八、快取
