review引發(fā)的有關(guān)于單例模式的思考
一次代碼調(diào)試中發(fā)現(xiàn)一個(gè)情況,即我在查看memcached的connection時(shí),發(fā)現(xiàn)總是維持在100來個(gè)左右,當(dāng)然這看似沒什么問題,因?yàn)閙emcached默認(rèn)connection有1024個(gè)。但是我想的是為什么會有100來個(gè),因?yàn)槲业膍emcachedclient的產(chǎn)生采用的是單例模式我定義了一個(gè)memcachedClientFactory類,主要代碼如下:
MemcachedClientFactory{
private MemcachedConnectionBuilder memcachedConnectionBuilder;
private String servers;
private static MemcachedClient memcachedClient;
private MemcachedClientFactory(){
}
private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
this. memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=servers;
}
public static MemcachedClient createClient(){
if(memcachedClient==null){
this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
return this.memcahcedClient;
}
}
}
回到最初的問題,為什么會有100多個(gè)連接?
上面這個(gè)寫法真的能保證只產(chǎn)生一個(gè)連接?很顯然是不能,為什么?多線程并發(fā)!問題就出在這里,當(dāng)有多個(gè)線程同時(shí)進(jìn)入createClient()方法時(shí),而且剛好都判斷為memcachedClient為null,這時(shí)候就產(chǎn)生了多個(gè)連接。哈,問題找到了。
改進(jìn):
public static synchronizd MemcachedClient createClient(){
if(memcachedClient==null){
this.memcahcedClien= new
MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
return this.memcahcedClient;
}
這樣就ok了,改動(dòng)很簡單。程序是沒有問題了,而且也能保證只有一個(gè)連接。
不過拋開這個(gè)問題,我們可以繼續(xù)就如何解決單例模式下的并發(fā)問題深入思考一下。
我總結(jié)一下,要解決單例模式在并發(fā)下的問題,大概有三種方式:
1. 不使用延遲實(shí)例化,而是用提前實(shí)例化。
即程序改寫為:
Public Class Singleton{
private static Singleton instance=new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
這樣做時(shí),jvm在加載類時(shí)就立馬創(chuàng)建了該實(shí)例,所以這樣做的前提是,創(chuàng)建該實(shí)例的負(fù)擔(dān)不大,我不比過多的考慮性能,并且我們確認(rèn)該實(shí)例是一定會用到的。其實(shí)我前面的代碼也完全可以使用這個(gè)方式:
MemcachedClientFactory{
private MemcachedConnectionBuilder memcachedConnectionBuilder;
private String servers;
private static MemcachedClient memcachedClien= new
MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
private MemcachedClientFactory(){
}
private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
this. memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=servers;
}
public static MemcachedClient createClient(){
return this.memcahcedClient;
}
}
}
不過,看上去似乎沒有問題,但是有隱患,即一旦有人不小心調(diào)用了memcachedClient.shutdown()方法,那整個(gè)程序就無法再生出新的memcachedClient了。當(dāng)然這是極端情況了,但是為了代碼的健壯,可以再改為:
public static MemcachedClient createClient(){
if(memcachedClient==null){
this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
return this.memcahcedClient;
}
2. 就是使用synchronized關(guān)鍵字。
這么做可以保證同步問題,但是我們知道使用synchronized的開銷是很大的,會嚴(yán)重影響性能,所以用這個(gè)的前提是,你確認(rèn)不會經(jīng)常調(diào)用這個(gè)方法,或者你創(chuàng)建這個(gè)instance的開銷不會特別大。是否還可以改進(jìn),看 下面。
3. 使用“雙重檢查加鎖“,在getInstance中見識使用同步
public Class Singleton{
private volatile static Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
相關(guān)文章
SpringMVC中的HandlerMapping和HandlerAdapter詳解
這篇文章主要介紹了SpringMVC中的HandlerMapping和HandlerAdapter詳解,在Spring MVC中,HandlerMapping(處理器映射器)用于確定請求處理器對象,請求處理器可以是任何對象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下2023-08-08spring boot項(xiàng)目application.properties文件存放及使用介紹
這篇文章主要介紹了spring boot項(xiàng)目application.properties文件存放及使用介紹,我們的application.properties文件中會有很多敏感信息,大家在使用過程中要多加小心2021-06-06java如何獲取兩個(gè)List集合之間的交集、差集、并集
在日常開發(fā)中經(jīng)常會遇到對2個(gè)集合的操作,例如2個(gè)集合之間取相同的元素(交集),2個(gè)集合之間取不相同的元素(差集)等等,這篇文章主要給大家介紹了關(guān)于java如何獲取兩個(gè)List集合之間的交集、差集、并集的相關(guān)資料,需要的朋友可以參考下2024-02-02SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的方法示例
這篇文章主要介紹了SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10