自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

女朋友驚掉下巴問我:?jiǎn)卫J骄褂衅叻N寫法?

開發(fā) 前端
接下來,我們要進(jìn)入的是設(shè)計(jì)模式篇,關(guān)于設(shè)計(jì)模式,作為程序員的你,肯定在工作中或者面試中遇到過很多次了吧!

[[421641]]

前言

接下來,我們要進(jìn)入的是設(shè)計(jì)模式篇,關(guān)于設(shè)計(jì)模式,作為程序員的你,肯定在工作中或者面試中遇到過很多次了吧

記得當(dāng)時(shí)18年上大三的時(shí)候出去找實(shí)習(xí),也問過了解哪些設(shè)計(jì)模式,不過我個(gè)人回答的最多的最詳細(xì)的大概也就是單例模式了,因?yàn)槲矣X得這個(gè)應(yīng)該是最最好理解的了,雖然有很多種寫法,這是為了解決不同環(huán)境下的不同問題,當(dāng)時(shí)我應(yīng)該是把懶漢、餓漢直接都手撕了一遍,也簡(jiǎn)單的把懶漢和餓漢的區(qū)別說了說

當(dāng)時(shí)令我吃驚的是面試官告訴我,單例模式其實(shí)有七種寫法,甚至可以更多,我當(dāng)時(shí)驚得下巴都掉了,當(dāng)時(shí)我就感覺到了這個(gè)行業(yè)滿滿的挑戰(zhàn)和滿滿的知識(shí)等著我學(xué)習(xí)

果不其然,現(xiàn)在越學(xué)越覺得自己廢物,越學(xué)越感覺自己有太多不會(huì)的了,不過這個(gè)路肯定還是要走下去的,撥開云霧見天明,堅(jiān)持下去吧

接下來我們來簡(jiǎn)單介紹下單例模式

單例模式,顧名思義,就是唯一的實(shí)例。在當(dāng)前進(jìn)程中,有且只有一個(gè)單例模式創(chuàng)建的類對(duì)象

比如生活中的太陽、只能有一個(gè)吧,所以只能有一個(gè)實(shí)例,這個(gè)例子要是用在當(dāng)年后羿射箭之前不合適,但是現(xiàn)在應(yīng)該還算是合適的吧

再比如寫一個(gè)校園管理系統(tǒng),有一個(gè)校長(zhǎng)的角色,只能有一個(gè),這個(gè)對(duì)象在該系統(tǒng)中做成單例就比較合適(其余的是副校長(zhǎng)的 親

這個(gè)模式應(yīng)該是大家最常見的,也是大家認(rèn)為最簡(jiǎn)單的了吧,但是實(shí)際上這個(gè)模式里面還是有很多細(xì)節(jié)的,也有很多的點(diǎn)值得大家思考的,待會(huì)咱們一起看各種寫法的時(shí)候大家記得帶著你的思考和你的問題去學(xué)習(xí)

正文

單例模式特點(diǎn)

單例模式有如下的特點(diǎn):

1、一個(gè)JVM中有且只有一個(gè)實(shí)例的存在,構(gòu)造器私有,外部無法創(chuàng)建該實(shí)例

2、提供一個(gè)公開的get方法獲得唯一的這個(gè)實(shí)例

有哪些優(yōu)點(diǎn)呢:

1、省去了new的操作,降低系統(tǒng)內(nèi)存的使用頻率,減輕GC的壓力

2、系統(tǒng)中的一些類需要全局單例,比如spring中的controller,再比如人類的太陽

3、避免了資源的重復(fù)的占用,減少了內(nèi)存的開銷

其實(shí)也是有一些缺點(diǎn)的:

沒有接口,不可繼承與單一職責(zé)原則沖突,一個(gè)類應(yīng)該只關(guān)心內(nèi)部邏輯,而不關(guān)心外面怎么樣來實(shí)例化

先把要介紹的七種給大家說一下,大家有個(gè)印象

餓漢式、懶漢式線程不安全和安全版、DCL雙重檢測(cè)鎖模式的線程不安全和安全版、靜態(tài)內(nèi)部類、枚舉類

大家先聽個(gè)耳熟,下面一一介紹

餓漢式

餓漢式,就是比較餓,于是乎吃的比較早,也就是創(chuàng)建的比較早,會(huì)隨著JVM的啟動(dòng)而初始化該單例

也正是由于這種類裝載的時(shí)候就完成了單例的實(shí)例化了,不存在所謂的線程安全問題,是線程安全的,相應(yīng)的缺點(diǎn)就是未達(dá)到lazy loading的效果,如果創(chuàng)建的這個(gè)單例類始終未用到,便回造成資源浪費(fèi)

其實(shí)在實(shí)際開發(fā)中,即使知道一定用得到,我們一般也不太會(huì)使用這種機(jī)制,因?yàn)槿绻麊卫龑?duì)象很多,會(huì)影響啟動(dòng)的速度,采用懶加載機(jī)制是比較節(jié)約資源的

開發(fā)中很多思想也是采用懶加載,只有當(dāng)真正用到一個(gè)東西的時(shí)候才允許它占用相應(yīng)的資源

  1. /** 
  2.  * 餓漢式:通過classloader機(jī)制避免了多線程的同步問題,在類裝載的時(shí)候完成實(shí)例化 
  3.  * 優(yōu)點(diǎn):寫法簡(jiǎn)單,類裝載的時(shí)候完成實(shí)例化,避免了線程同步的問題 
  4.  * 缺點(diǎn):未達(dá)到lazy loading的效果,如果始終未用到則可能造成資源浪費(fèi) 
  5.  * 適用場(chǎng)景: 
  6.  */ 
  7. public class HungrySingleton { 
  8.  
  9.     //1、構(gòu)造器私有化 
  10.     private HungrySingleton(){} 
  11.     //2、類的內(nèi)部創(chuàng)建對(duì)象的實(shí)例 
  12.     private final static HungrySingleton dayu = new HungrySingleton(); 
  13.     //3、將類的內(nèi)部實(shí)例提供一個(gè)靜態(tài)方法返回出去 
  14.     private static HungrySingleton getInstance(){ 
  15.         return dayu; 
  16.     } 
  17.  

懶漢式(線程不安全、線程安全)

懶漢式咯,就是比較懶,在啟動(dòng)的時(shí)候,不會(huì)進(jìn)行該單例對(duì)象的創(chuàng)建,只有當(dāng)真正用到的時(shí)候才會(huì)去加載這些東西

之所以加懶漢式,大概就是采用了懶加載思想

我們看下面這個(gè)懶漢式的代碼

  1. /** 
  2.  * 懶漢式 
  3.  * 缺點(diǎn):線程不安全,工作中一般不用 
  4.  */ 
  5. public class NotSafeLazySingleton { 
  6.     //構(gòu)造器私有化 
  7.     private NotSafeLazySingleton(){} 
  8.     //暫時(shí)不加載實(shí)例 
  9.     private static NotSafeLazySingleton dayu; 
  10.  
  11.     /** 
  12.      * 存在線程安全問題 
  13.      * 線程A到括號(hào)dayu == null判斷完之后,進(jìn)入括號(hào)內(nèi)部, 
  14.      * 此時(shí)線程B獲得執(zhí)行權(quán),判斷==null也是true,所以也進(jìn)入 
  15.      * 此時(shí)兩個(gè)線程便出現(xiàn)了兩個(gè)dayu對(duì)象 
  16.      * @return 
  17.      */ 
  18.     public static NotSafeLazySingleton getInstance(){ 
  19.         if(dayu == null){ 
  20.             dayu = new NotSafeLazySingleton(); 
  21.         } 
  22.         return dayu; 
  23.     } 

其實(shí)有過多線程的經(jīng)驗(yàn)的小伙伴應(yīng)該很快就看出來了,上面這種懶漢式是有線程安全問題的,當(dāng)線程A執(zhí)行到if(dayu == null)這一行的時(shí)候,判斷為空,true進(jìn)入括號(hào)內(nèi)部,此時(shí)線程A的時(shí)間片用完了,到了線程B的執(zhí)行了,于是乎也會(huì)判斷為空,進(jìn)入括號(hào)內(nèi)部

線程B創(chuàng)建了一個(gè)NotSafeLazySingleton對(duì)象,輪到線程A執(zhí)行的時(shí)候,由于在之前已經(jīng)判斷完進(jìn)入了括號(hào)內(nèi)部,于是線程A也會(huì)創(chuàng)建一個(gè)NotSafeLazySingleton對(duì)象

GG,這樣不是我們想要的效果,這就不屬于單例模式了,所以這種在多線程情況下是存在安全問題的

有了問題,自然就是解決咯,可能有的小伙伴也想到了,存在線程安全問題,那就加上線程安全關(guān)鍵字synchronized來解決,于是乎便有了下面的代碼,我們給函數(shù)加上關(guān)鍵字synchronized,但是這樣會(huì)造成效率極其低下

所有調(diào)用這個(gè)方法去使用單例對(duì)象的地方都需要排隊(duì)阻塞知道該鎖的釋放,在多線程情況下會(huì)迅速降低效率

  1. /** 
  2.  * 懶漢式安全寫法 
  3.  * 缺點(diǎn):Synchronized關(guān)鍵字導(dǎo)致方法效率低 效率極低 
  4.  * 優(yōu)點(diǎn):線程安全 
  5.  * 適用場(chǎng)景:實(shí)際開發(fā) 不推薦使用 
  6.  */ 
  7. public class SafeLazySingleton { 
  8.     //構(gòu)造器私有化 
  9.     private SafeLazySingleton(){} 
  10.     //暫時(shí)不加載實(shí)例 
  11.     private static SafeLazySingleton dayu; 
  12.  
  13.     /** 
  14.      * synchronized導(dǎo)致所有通過該方法獲取該對(duì)象的時(shí)候都要排隊(duì) 
  15.      */ 
  16.     public static synchronized SafeLazySingleton getInstance(){ 
  17.         if(dayu == null){ 
  18.             dayu = new SafeLazySingleton(); 
  19.         } 
  20.         return dayu; 
  21.     } 

所有調(diào)用這個(gè)方法去使用單例對(duì)象的地方都需要排隊(duì)阻塞知道該鎖的釋放,在多線程情況下會(huì)迅速降低效率,于是有了下面的這種改進(jìn)方法

只鎖其中的部分代碼,看下下面的代碼

  1. /** 
  2.  *  本意上是對(duì)SafeLazySingelton的改進(jìn) 因?yàn)榍懊娴膶?duì)整個(gè)方法進(jìn)行加鎖的效率實(shí)在是太低了 
  3.  *  但是這種還是不能起到線程同步的作用 和NotSafeLazySingelton類似 只要線程進(jìn)入了== null的里面 
  4.  *  此時(shí)另一個(gè)線程獲得CPU分配的時(shí)間片 則會(huì)出現(xiàn)多個(gè)對(duì)象 
  5.  */ 
  6. public class NotSafeLaySingleton2 { 
  7.  
  8.     //構(gòu)造器私有化 
  9.     private NotSafeLaySingleton2(){} 
  10.     //暫時(shí)不加載實(shí)例 
  11.     private static NotSafeLaySingleton2 dayu; 
  12.  
  13.     /** 
  14.      * @return 
  15.      */ 
  16.     public static NotSafeLaySingleton2 getInstance(){ 
  17.         if(dayu == null){ 
  18.             synchronized (NotSafeLaySingleton2.class){ 
  19.                 dayu = new NotSafeLaySingleton2(); 
  20.             } 
  21.         } 
  22.         return dayu; 
  23.     } 

上面的這種代碼看著有問題嗎?

不知道你認(rèn)真讀了上面代碼之后,內(nèi)心是怎么想的,聰明的小伙伴已經(jīng)發(fā)現(xiàn)了事情不是這么簡(jiǎn)單,發(fā)現(xiàn)其中了問題

是的,上面的這種改進(jìn)方法,貌似實(shí)現(xiàn)了效率跟高些,但是會(huì)隨之帶來多線程的問題

線程A判斷dayu == null進(jìn)入括號(hào),還沒拿到NotSafeLaySingleton2的鎖,時(shí)間片消耗完了,此時(shí)線程B也判斷,發(fā)現(xiàn)dayu == null也成立,此時(shí)也會(huì)進(jìn)入括號(hào),假設(shè)線程B拿到了鎖,創(chuàng)建了一個(gè)NotSafeLaySingleton2對(duì)象,執(zhí)行完之后釋放鎖。線程A拿到該鎖,會(huì)重新創(chuàng)建一個(gè)對(duì)象,于是出現(xiàn)多例現(xiàn)象

先是通過synchronized加在方法層面解決并發(fā)問題,但是隨之而來帶來效率問題,于是為了提高效率,加在內(nèi)部,但是加在內(nèi)部就有了相應(yīng)的線程安全問題

說了這么多,就是要引出我們下面的線程安全的DCL的單例模式

看下怎么寫

雙重檢查鎖模式DCL- double chechked locking(線程安全)

上面那個(gè)其實(shí)屬于單重檢查鎖模式,我起的名字,因?yàn)橹粰z查了一個(gè)地方的鎖,正是如此也帶來了多線程的問題,于是乎就有了下面這種雙重檢測(cè)形勢(shì)的單例模式了,一起看看吧,穩(wěn)得一批

  1. /** 
  2.  * 雙重檢測(cè)單例:穩(wěn)得一批 
  3.  * 優(yōu)點(diǎn):線程安全 延遲加載 效率相對(duì)來說也不錯(cuò) 
  4.  *使用場(chǎng)景:實(shí)際開發(fā)中 用的比較多 
  5.  */ 
  6. public class DoubleCheckSingleton { 
  7.  
  8.     private static volatile DoubleCheckSingleton dayu; 
  9.     private DoubleCheckSingleton(){} 
  10.     /** 
  11.      * 解決線程安全的問題同時(shí) 也解決懶加載問題 
  12.      * @return 
  13.      */ 
  14.     public static DoubleCheckSingleton getInstance(){ 
  15.         if(dayu == null){ 
  16.             synchronized (DoubleCheckSingleton.class){ 
  17.                 if(dayu == null){ 
  18.                     dayu = new DoubleCheckSingleton(); 
  19.                 } 
  20.             } 
  21.         } 
  22.         return dayu; 
  23.     } 

上面這種在進(jìn)入了data == null的內(nèi)部也會(huì)再次判斷一次是否還等于空,這種就很好的解決了多線程的問題

這種DCL的單例模式在工作中算是常用的一種了,有效的解決高并發(fā)下的單例模式問題

靜態(tài)內(nèi)部類

靜態(tài)內(nèi)部類加載單例,類加載機(jī)制保證線程安全,而且還有一個(gè)優(yōu)點(diǎn),懶加載,只有在調(diào)用getInstance的時(shí)候才會(huì)加載內(nèi)部類,才會(huì)創(chuàng)建這個(gè)對(duì)象

外部類被裝載的時(shí)候,內(nèi)部類不會(huì)立即被裝載,調(diào)用getInstance才會(huì)裝載,并且只會(huì)裝載一次,且不存在線程安全問題

  1. /** 
  2.  * 靜態(tài)內(nèi)部類加載單例 
  3.  * 優(yōu)點(diǎn):類裝載機(jī)制保證線程安全 懶加載 只有調(diào)用getInstance才會(huì)加載內(nèi)部類 
  4.  * 適用場(chǎng)景: 
  5.  */ 
  6. public class StaticInnerClassSingleton { 
  7.     private StaticInnerClassSingleton(){} 
  8.  
  9.     /** 
  10.      * 1、外部類被裝載時(shí)  內(nèi)部不會(huì)立即被裝載 
  11.      * 2、調(diào)用getInstance方法時(shí)會(huì)裝載 只會(huì)裝載一次 且不存在線程安全 
  12.      */ 
  13.     private static class SingletonInstance{ 
  14.         private static final StaticInnerClassSingleton dayu = new StaticInnerClassSingleton(); 
  15.     } 
  16.     //返回靜態(tài)內(nèi)部類中的對(duì)象 
  17.     public static StaticInnerClassSingleton getInstance(){ 
  18.         return SingletonInstance.dayu; 
  19.     } 

枚舉類

枚舉類也是可以用作單例模式,而且還很簡(jiǎn)單

Effective Java作者Josh Bloch所提倡的單例實(shí)現(xiàn)的方式就是這種,這種無線程安全問題,還可以防止反序列化重新創(chuàng)建新的對(duì)象

  1. /** 
  2.  * 枚舉實(shí)現(xiàn)單例 
  3.  * 優(yōu)點(diǎn):簡(jiǎn)潔 無線程安全問題 還可以防止反序列化重新創(chuàng)建新的對(duì)象 
  4.  * Effective Java作者Josh Bloch提倡的方法 
  5.  */ 
  6. public class EnumSingleton { 
  7.  
  8.     public static void main(String[] args) { 
  9.         //instance和instance2是同一個(gè)對(duì)象 
  10.         Singleton instance = Singleton.INSTANCE; 
  11.         Singleton instance2 = Singleton.INSTANCE; 
  12.     } 
  13.  
  14.     enum Singleton{ 
  15.         INSTANCE; 
  16.     } 

總結(jié)

設(shè)計(jì)模式應(yīng)該屬于面試高頻,而單例模式又是設(shè)計(jì)模式的最簡(jiǎn)單,或者說是最常見的設(shè)計(jì)模式之一,看完這篇文章,大家應(yīng)該都知道單例模式的多種寫法了,也知道各種的優(yōu)劣勢(shì)和相應(yīng)的使用場(chǎng)景了

我們思考一個(gè)問題,為什么要使用單例模式而使用靜態(tài)方法

這兩個(gè)其實(shí)都可以實(shí)現(xiàn)我們加載的最終目的,但是他們一個(gè)是基于對(duì)象的,一個(gè)是屬于面向?qū)ο蟮?,就像是很多種情況,我們通過普通的編碼也可以實(shí)現(xiàn),但是我們引入設(shè)計(jì)模式來更好的體現(xiàn)編程思想

如果一個(gè)方法和他所在的類的實(shí)例對(duì)象確實(shí)是無關(guān)的,那么它就應(yīng)該是靜態(tài)的,反之它就應(yīng)該是非靜態(tài)的,如果我們需要使用非靜態(tài)的方法,但是在創(chuàng)建類對(duì)象的時(shí)候,又只需要維護(hù)一個(gè)實(shí)例,不想創(chuàng)建多個(gè)不同的實(shí)例,就需要使用單例模式了。

 

責(zé)任編輯:武曉燕 來源: 大魚仙人
相關(guān)推薦

2020-03-18 09:31:47

設(shè)計(jì)模式軟件

2022-08-10 11:02:56

Python單例模式

2020-10-15 09:35:27

亂碼UTF-8GBK

2020-12-14 10:25:08

DNS通信IP

2023-12-22 14:27:30

2024-12-03 16:49:58

2022-05-23 07:35:15

單例模式懶漢模式靜態(tài)內(nèi)部類

2021-04-06 06:23:18

MVCC并發(fā)事務(wù)

2020-09-08 08:55:52

Dubbo服務(wù)全鏈路

2024-12-04 13:30:00

2025-04-17 09:31:41

2022-04-26 05:55:52

網(wǎng)絡(luò)網(wǎng)絡(luò)類型

2018-03-19 14:54:14

程序員朋友圈技術(shù)

2018-11-05 09:13:00

ARM處理器模式

2010-10-15 10:02:01

Mysql表類型

2019-10-29 06:30:31

告警疲勞網(wǎng)絡(luò)安全安全風(fēng)險(xiǎn)

2022-05-10 08:08:01

find命令Linux

2020-01-14 08:00:00

.NET緩存編程語言

2021-03-02 08:50:31

設(shè)計(jì)單例模式

2010-06-08 09:49:45

UML元件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)