單例模式寫法分享
雙重判定鎖寫法
public class Singleton implements Serializable {
private static volatile Singleton singleton = null;
private Singleton(){
if (singleton != null) {
throw new Exception(“該實例已生成,禁止被反射調用生成”)
}
}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
private Object readResolve() throws ObjectStreamException {
return singleton;
}
}
靜態內部類寫法:
public class Singleton {
private transient static class Holder {
private static Singleton singleton = new Singleton();
}
private Singleton(){
if (singleton != null) {
throw new Exception(“該實例已生成,禁止被反射調用生成”)
}
}
public static Singleton getSingleton(){
return Holder.singleton;
}
}
上述兩寫法缺點:都需要額外的工作(Serializable、transient、readResolve())來實現序列化,否則每次反序列化一個序列化的對象實例時都會創建一個新的實例
枚舉寫法 Joshua Bloch的推薦:
public enum Singleton {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
Effective Java推薦儘可能地使用枚舉來實現單例。
Spring,單例註冊表:
public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
/**
* 充當了Bean實例的緩存,實現方式和單例註冊表相同
*/
private final Map singletonCache=new HashMap();
public Object getBean(String name)throws BeansException{
return getBean(name,null,null);
}
...
public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{
//對傳入的Bean name稍做處理,防止傳入的Bean name名有非法字符(或則做轉碼)
String beanName=transformedBeanName(name);
Object bean=null;
//手工檢測單例註冊表
Object sharedInstance=null;
//使用了代碼鎖定同步塊,原理和同步方法相似,但是這種寫法效率更高
synchronized(this.singletonCache){
sharedInstance=this.singletonCache.get(beanName);
}
if(sharedInstance!=null){
...
//返回合適的緩存Bean實例
bean=getObjectForSharedInstance(name,sharedInstance);
}else{
...
//取得Bean的定義
RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);
...
//根據Bean定義判斷,此判斷依據通常來自於組件配置文件的單例屬性開關
//<bean id="date" class="java.util.Date" scope="singleton"/>
//如果是單例,做如下處理
if(mergedBeanDefinition.isSingleton()){
synchronized(this.singletonCache){
//再次檢測單例註冊表
sharedInstance=this.singletonCache.get(beanName);
if(sharedInstance==null){
...
try {
//真正創建Bean實例
sharedInstance=createBean(beanName,mergedBeanDefinition,args);
//向單例註冊表註冊Bean實例
addSingleton(beanName,sharedInstance);
}catch (Exception ex) {
...
}finally{
...
}
}
}
bean=getObjectForSharedInstance(name,sharedInstance);
}
//如果是非單例,即prototpye,每次都要新創建一個Bean實例
//<bean id="date" class="java.util.Date" scope="prototype"/>
else{
bean=createBean(beanName,mergedBeanDefinition,args);
}
}
return bean;
}
}