9種Java單例模式詳解(推薦)
單例模式的特點(diǎn)
- 一個(gè)類只允許產(chǎn)生一個(gè)實(shí)例化對(duì)象。
- 單例類構(gòu)造方法私有化,不允許外部創(chuàng)建對(duì)象。
- 單例類向外提供靜態(tài)方法,調(diào)用方法返回內(nèi)部創(chuàng)建的實(shí)例化對(duì)象。
懶漢式(線程不安全)
其主要表現(xiàn)在單例類在外部需要?jiǎng)?chuàng)建實(shí)例化對(duì)象時(shí)再進(jìn)行實(shí)例化,進(jìn)而達(dá)到Lazy Loading 的效果。
通過(guò)靜態(tài)方法 getSingleton() 和private 權(quán)限構(gòu)造方法為創(chuàng)建一個(gè)實(shí)例化對(duì)象提供唯一的途徑。
不足:未考慮到多線程的情況下可能會(huì)存在多個(gè)訪問(wèn)者同時(shí)訪問(wèn),發(fā)生構(gòu)造出多個(gè)對(duì)象的問(wèn)題,所以在多線程下不可用這種方法。
/**
* @author MrRoot
* @since 2018-12-17
* 懶漢式(線程不安全)
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton singleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
懶漢式(線程安全,同步方法,不推薦使用)
針對(duì)懶漢式的線程不安全,自然會(huì)想到給 getSingleton() 進(jìn)行 synchronized 加鎖來(lái)保證線程同步。
不足:效率低。大多數(shù)情況下這個(gè)鎖占用的額外資源都浪費(fèi)了,每個(gè)線程在想獲得類的實(shí)例時(shí)候,執(zhí)行 getSingleton() 方法都要進(jìn)行同步。
/**
* @author MrRoot
* @since 2019-3-27
* 懶漢式(線程安全,同步方法,不推薦使用)
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static synchronized Singleton singleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
餓漢式(線程安全)
在進(jìn)行類加載時(shí)完成實(shí)例化對(duì)象的過(guò)程就是餓漢式的形式。
避免了線程同步問(wèn)題,在運(yùn)行這個(gè)類的時(shí)候進(jìn)行加載,之后直接訪問(wèn)
不足:相比接下來(lái)的靜態(tài)內(nèi)部類而言,這種方法比靜態(tài)內(nèi)部類多了內(nèi)存常駐,容易造成內(nèi)存浪費(fèi),也未達(dá)到延遲加載的效果。
/**
* @author MrRoot
* @since 2019-3-27
* 餓漢式(線程安全)
*/
public class Singleton{
private static Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton singleton(){
return singleton;
}
}
靜態(tài)內(nèi)部類加載(線程安全)
靜態(tài)內(nèi)部類不會(huì)在單例加載時(shí)加載,當(dāng)調(diào)用 getSingleton() 方法時(shí)才會(huì)進(jìn)行加載,達(dá)到類似懶漢式效果,并且也是線程安全的。
類的靜態(tài)屬性只會(huì)在第一次加載類時(shí)進(jìn)行初始化,所以上面的方法JVM 幫助我們保證了線程的安全性,在類進(jìn)行初始化時(shí),其他線程無(wú)法進(jìn)入。
/**
* @author MrRoot
* @since 2019-3-27
* 靜態(tài)內(nèi)部類加載(線程安全)
*/
public class Singleton{
private static Singleton singleton;
private static class SingletonInner{
private static final Singleton instance = new Singleton();
}
public static Singleton getSingleton(){
return SingletonInner.instance;
}
}
枚舉(線程安全)
自由串行化;保證只有一個(gè)實(shí)例;線程安全。
Effective Java 作者所提倡的方法,近乎完美,在繼承場(chǎng)景下不適用。
/**
* @author MrRoot
* @since 2019-3-27
* 枚舉(線程安全)
*/
enum Singleton{
INSTANCE;
public void method(){
}
}
class Test{
public static void main(String[] args) {
Singleton.INSTANCE.method();
}
}
懶漢式雙重校驗(yàn)鎖法(通常線程安全,不可保證完全安全)
使用同步代碼塊避免了第二種方法的效率低的問(wèn)題,但此方法并不能完全起到線程同步的作用,與上面第一種方法產(chǎn)生的問(wèn)題相似,多線程訪問(wèn)時(shí)可能產(chǎn)生多個(gè)對(duì)象。
/**
* @author MrRoot
* @since 2019-3-27
* 懶漢式雙重校驗(yàn)鎖法(通常線程安全,不可保證完全安全)
*/
class Singleton{
private static Singleton singleton;
private Singleton(){
}
public static Singleton singleton(){
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
懶漢式雙重檢查終極版
與第六種方法不同的是,此方法給singleton 的聲明上加了關(guān)鍵字 volatile ,進(jìn)而解決了低概率的線程不安全問(wèn)題。
volatile 起到禁止指令重排的作用,在它賦值完成之前,就不會(huì)調(diào)用讀操作(singleton == null)。
/**
* @author MrRoot
* @since 2019-3-27
* 懶漢式雙重檢查終極版(volatile)
*/
class Singleton{
private static volatile Singleton singleton;
private Singleton(){
}
public static Singleton singleton(){
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
使用 ThreadLocal 實(shí)現(xiàn)(線程安全)
ThreadLocal 會(huì)為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程對(duì)數(shù)據(jù)的訪問(wèn)沖突。
對(duì)于多線程資源共享的問(wèn)題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal 采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問(wèn),而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問(wèn)而互不影響。
/**
* @author MrRoot
* @since 2019-3-27
* 使用 ThreadLocal 實(shí)現(xiàn)(線程安全)
*/
class Singleton{
private static final ThreadLocal<Singleton> singleton = new
ThreadLocal<Singleton>(){
@Override
protected Singleton initialValue(){
return new Singleton();
}
};
private Singleton(){
}
public static Singleton getSingleton(){
return singleton.get();
}
}
使用CAS 鎖實(shí)現(xiàn)(線程安全)
/**
* @author MrRoot
* @since 2019-3-27
* 使用 CAS 實(shí)現(xiàn)(線程安全)
*/
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private Singleton(){
}
public static final Singleton getSingleton(){
for (;;){
Singleton current = INSTANCE.get();
if (current != null){
return current;
}
current = new Singleton();
if (INSTANCE.compareAndSet(null,current)){
return current;
}
}
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1 == singleton2);
}
}
以上所述是小編給大家介紹的9種Java單例模式詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Spring Cloud Gateway 內(nèi)存溢出的解決方案
這篇文章主要介紹了Spring Cloud Gateway 內(nèi)存溢出的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過(guò)程解析
這篇文章主要介紹了spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
spring-boot-starter-web更換默認(rèn)Tomcat容器的方法
Spring Boot支持容器的自動(dòng)配置,默認(rèn)是Tomcat,當(dāng)然我們也是可以進(jìn)行修改的。下面小編給大家?guī)?lái)了spring-boot-starter-web更換默認(rèn)Tomcat容器的方法,感興趣的朋友跟隨小編一起看看吧2019-04-04
淺談@Value和@Bean的執(zhí)行順序問(wèn)題
這篇文章主要介紹了@Value和@Bean的執(zhí)行順序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
spring AOP實(shí)現(xiàn)@Around輸出請(qǐng)求參數(shù)和返回參數(shù)
這篇文章主要介紹了spring AOP實(shí)現(xiàn)@Around輸出請(qǐng)求參數(shù)和返回參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
解決idea默認(rèn)帶的equals和hashcode引起的bug
這篇文章主要介紹了解決idea默認(rèn)帶的equals和hashcode引起的bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

