摘要:單例模式確保一個類只有一個實例,而且自動實例化并向整個系統提供這個實例。將構造函數設置為私有的,防止外界出該類的實例,從而失去了單例的意義。這種實現的單例模式是最簡單的,同時多個線程操作該單例時也不會有問題。這就違反了單例模式。
單例模式
確保一個類只有一個實例,而且自動實例化并向整個系統提供這個實例。
實現 餓漢式很簡單。
將構造函數設置為私有的,防止外界new出該類的實例,從而失去了單例的意義。
設置類的私有靜態變量,同時新建單例對象。
添加共有靜態方法獲取該單例。
該種方法的缺點是在類加載時就進行實例化,但是相較于其簡單易用來說,這點缺點個人認為影響不大。
package com.mengyunzhi; /** * @author zhangxishuo on 2018/6/18 */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
這種實現的單例模式是最簡單的,同時多個線程操作該單例時也不會有問題。
package com.mengyunzhi; public class Main { public static void main(String[] args) { Thread thread1 = new Thread(() -> { Singleton singleton1 = Singleton.getInstance(); System.out.println("Singleton1:" + singleton1); }); Thread thread2 = new Thread(() -> { Singleton singleton2 = Singleton.getInstance(); System.out.println("Singleton2:" + singleton2); }); thread1.start(); thread2.start(); } }
注:打印時調用toString方法,因為沒有重寫toString,調用Object類中的toString,所以打印該對象的類名加哈希值。
我們看到控制臺中打印的兩個對象地址都是89ae60d,表示同一塊內存,即表示多線程時該實現方法仍能實現單例。
線程競爭我們調用的順序明明是thread1的start,然后thread2再start,但是為什么控制臺打印的順序卻是單例2和單例1呢?
這兩個線程會競爭處理器的資源,這里打印的順序是單例2、單例1,說明處理器處執行線程時,先執行完thread2線程,后執行完thread1線程。這兩個線程可能是同時執行,也可能是來回切換執行,這取決于處理器的核心與線程。
代碼講解函數式接口
Thread類的構造函數接收的是一個Runnable接口類型的參數,所以之前創建線程的代碼長這樣。
Thread myThread = new Thread(new Runnable() { @Override public void run() { } });
看下面的代碼,因為Runnable接口只有一個抽象的run方法需要去實現,所以就不需要去@Override聲明我要實現run方法,直接傳一個函數體不就可以嗎?這就是函數式接口。
package java.lang; @FunctionalInterface public interface Runnable { public abstract void run(); }
lamda表達式
相信很多人都聽說過lamda表達式,但是總是覺得這個很高大上,其實我們接觸過lamda表達式,只是沒有注意到。
self.init = function() { };
在JavaScript的世界里,我們可以將一個函數傳來傳去。
self.init = () => { };
然后人們發現,寫function太麻煩了,他們發明了箭頭函數。用這種寫法代替一個函數。
那Java為什么不可以?
如果剛剛的代碼這么寫,那你應該瞬間就明白了。
Runnable runnable = () -> { Singleton singleton1 = Singleton.getInstance(); System.out.println("Singleton1:" + singleton1); }; Thread thread1 = new Thread(runnable);懶漢式
這是懶漢式的寫法,當需要這個實例的時候,再去新建實例。
package com.mengyunzhi; /** * @author zhangxishuo on 2018/6/18 */ public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
但是這是這種寫法是線程不安全的,我們再運行一下主函數中多個線程同時訪問單例的方法。
上次向晨澍請教:StringBuffer線程安全,StringBuilder線程不安全。既然有的類線程安全,有的類線程不安全?那為什么不都用線程安全的呢?
答案就是:為了實現線程安全,系統需要額外的開銷。所以有些不需要多線程的,使用線程不全的類,通常會提高速度。
假設我們的處理器支持多個線程并行處理,當多個線程同時訪問時,thread1獲取實例,然后判斷if (instance == null),創建實例;另一個線程同時執行,instance依然是空,然后thread2調用getInstance時又創建了一個實例。這就違反了單例模式。
synchronized
解決該問題的方案就是用synchronized修飾該代碼塊。
音標:["s??kr?na?zd]
只允許一個線程訪問synchronized修飾的代碼塊,其他線程會被阻塞,等待該線程執行完再執行。
synchronized public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
應該是線程2先執行完的,所以我們猜測就是:線程2競爭到處理器資源,然后去訪問getInstance()方法,因為筆者屬于多核多線程處理器,支持線程并行執行,當線程2訪問getInstance()代碼塊時,因為有synchronized修飾,所以線程1會被阻塞,等待線程2執行完再才能訪問該代碼塊。
線程2執行完創建實例,線程1可以訪問該代碼塊,發現instance不為空,直接返回。
使用場景頻繁new然后銷毀的對象,降低了內存開支。
當一個對象的產生需要較多資源時,如讀取配置,可以將其設置為單例,在應用啟動時產生一個單例對象常駐內存。
單例是同一個對象,可以用該單例設置項目配置,用于幾個模塊之間共享。
擴展:多線程學習 為什么要使用多線程?摩爾定律
每18個月,芯片的性能將提高一倍。
單核心
十幾年前,那時還是單核的時代,各大廠商做出主頻越來越高的處理器。
但是主頻越高,意味著芯片中需要的晶體管越多,功耗越大,散熱越多,當一定程度熱量就會燒壞芯片。
為什么CPU的頻率止步于4G?我們觸到頻率天花板了嗎?
2004年秋,Intel的CEO公開對取消4GHz芯片的計劃道歉。
這是Intel酷睿i7 8700K的參數,主頻僅有3.70GHz,十幾年過去了,我們依然停留在4GHz。
多核心
但是,為了滿足不斷增長的用戶需求,雖然無法提升單個核心的時鐘頻率,但是廠商利用多核心實現了芯片的性能提升。
這是Intel官網對i7 8700K的描述,6核心12線程。也就是說這個處理器有6個物理核心,因為超線程技術,可以模擬出12個邏輯核心,即可以同時處理12個線程任務。
超線程就是利用處理器剩余的資源模擬出一個新的核心,用于提高處理器的利用率。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71245.html
摘要:總結單例是運用頻率很高的模式,因為客戶端沒有高并發的情況,選擇哪種方式并不會有太大的影響,出于效率考慮,推薦使用和靜態內部類實現單例模式。 單例模式介紹 單例模式是應用最廣的模式之一,也可能是很多人唯一會使用的設計模式。在應用單例模式時,單例對象的類必須保證只用一個實例存在。許多時候整個系統只需要一個全局對象,這樣有利于我么能協調整個系統整體的行為。 單例模式的使用場景 確保某個類有且...
摘要:不符合設計模式中的單一職責的概念。引入代理實現單例模式引入代理實現單例模式的特點我們負責管理單例的邏輯移到了代理類中。的單例模式對比在以上的代碼中實現的單例模式都混入了傳統面向對象語言的特點。 聲明:這個系列為閱讀《JavaScript設計模式與開發實踐》 ----曾探@著一書的讀書筆記 1.單例模式的特點和定義 保證一個類僅有一個實例,并且提供一個訪問它的全局訪問點。 2.傳統面向對...
摘要:但是,這并不是采用單例的唯一原因。使用命名空間單例模式也被稱為模塊設計模式。函數內部聲明了一些局部函數和或變量。緊隨函數聲明放置即可立即執行外部函數,并將所得的對象文字費賠給變量。 JavaScript設計模式-第一部分:單例模式、組合模式和外觀模式 設計模式是一些可靠的編程方式,有助于保證代碼更加易于維護、擴展及分離,所有設計模式在創建大型JavaScript應用程序時均不可或缺 單...
摘要:如果需要防范這種攻擊,請修改構造函數,使其在被要求創建第二個實例時拋出異常。單例模式與單一職責原則有沖突。源碼地址參考文獻設計模式之禪 定義 單例模式是一個比較簡單的模式,其定義如下: 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。 或者 Ensure a class has only one instance, and provide a global point of ac...
摘要:在設計模式一書中,將單例模式稱作單件模式。通過關鍵字,來保證不會同時有兩個線程進入該方法的實例對象改善多線程問題為了符合大多數程序,很明顯地,我們需要確保單例模式能在多線程的情況下正常工作。 在《Head First 設計模式》一書中,將單例模式稱作單件模式。這里為了適應大環境,把它稱之為大家更熟悉的單例模式。 一、了解單例模式 1.1 什么是單例模式 單例模式確保一個類只有一個實例,...
閱讀 3734·2021-10-15 09:42
閱讀 2593·2021-09-03 10:50
閱讀 1628·2021-09-03 10:28
閱讀 1788·2019-08-30 15:54
閱讀 2510·2019-08-30 12:46
閱讀 401·2019-08-30 11:06
閱讀 2818·2019-08-30 10:54
閱讀 521·2019-08-29 12:59