单例模式归纳总结

一般的单例模式

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static Singleton getInstance() {    
  8.       if(instance == null) {    
  9.          instance = new Singleton();    
  10.       }    
  11.       return instance;    
  12.    }    
  13. }   
public class Singleton { 
   private static Singleton instance = null; 
  
   private Singleton() {  
   } 

   public static Singleton getInstance() { 
      if(instance == null) { 
         instance = new Singleton(); 
      } 
      return instance; 
   } 
} 


此方式已知问题:
1.如果由不同的ClassLoader去load,有可能存在多个实例。
2.线程不安全。假设线程1进入if判断,正在创建对象的时候,线程2进入,判断成功,这样就会创建2个对象实例。

改进方式,同步化getInstance方法,也就是懒汉式写法。

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static synchronized Singleton getInstance() {    
  8.       if(instance == null) {    
  9.          instance = new Singleton();    
  10.       }    
  11.       return instance;    
  12.    }    
  13. }  
public class Singleton { 
   private static Singleton instance = null; 
  
   private Singleton() {  
   } 

   public static synchronized Singleton getInstance() { 
      if(instance == null) { 
         instance = new Singleton(); 
      } 
      return instance; 
   } 
}


但getInstance会被外部线程比较频繁的调用,同步比非同步的性能消耗要昂贵很多,因此这样的做法会存在很大的额外性能消耗。因此产生另外一种改进方式,双重检查写法。

Java代码 复制代码
  1. public static Singleton getInstance() {    
  2.   if(singleton == null) {    
  3.      synchronized(Singleton.class) {    
  4.        if(singleton == null) {    
  5.          singleton = new Singleton();    
  6.        }    
  7.     }    
  8.   }    
  9.   return singleton;    
  10. }   
public static Singleton getInstance() { 
  if(singleton == null) { 
     synchronized(Singleton.class) { 
       if(singleton == null) { 
         singleton = new Singleton(); 
       } 
    } 
  } 
  return singleton; 
} 


此写法存在的问题在于singleton = new Singleton() 这句代码对于编译器来说,是分两步进行。首先初始化一个singleton对象并随意赋一个值,然后调用Singleton类的构造器赋值。因此在第一步完成后singleton == null这句判断已经不成立了,但此时singleton只是一个临时值。如果线程2此时进入getInstance,就会把这个singleton给返回出去。由于java的内存模型,双重检查并不能成功的起到作用。


采用饿汉式的写法也可避免线程安全问题.但是任何对Singleton类的访问(内部的static final变量除外,因为jvm会把它们直接编译为常量),比如另外一个static方法被访问,会引起jvm去初始化instance,而此时我们的本意是不想加载单例类的。同时因为没有延迟加载,最明显的缺点就是如果构造器内的方法比较耗时,则加载过程会比较长。对于一般的应用,构造方法内的代码不涉及到读取配置、远程调用、初始化IOC容器等长时间执行的情况,用这种方式是最简单的。

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = new Singleton();    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static Singleton getInstance() {    
  8.       return instance;    
  9.    }    
  10. }   
public class Singleton { 
   private static Singleton instance = new Singleton(); 
  
   private Singleton() {  
   } 

   public static Singleton getInstance() { 
      return instance; 
   } 
} 



如果我们既想使用延迟加载的好处,让类在被使用的时候才去加载,又想避免额外的同步调用开销,同时还不使用双重检查的模式,可以用初始化一个中间的容器类来解决这个问题。
就可以用如下的写法:

Java代码 复制代码
  1. public class Singleton {     
  2.    private static class SingletonHolder {      
  3.       static Singleton instance = new Singleton();          
  4.    }      
  5.       
  6.    public static Singleton getInstance() {      
  7.        return SingletonHolder.instance;      
  8.    }    
  9.   
  10.    private Singleton() {  }    
  11.   
  12. }   
public class Singleton {  
   private static class SingletonHolder {   
      static Singleton instance = new Singleton();       
   }   
   
   public static Singleton getInstance() {   
       return SingletonHolder.instance;   
   } 

   private Singleton() {  } 

} 


java中,只有一个类被用到的时候才被初始化。在getInstance方法被调用的时候,如果SingletonHolder类没有被加载,就会去加载,起到延迟加载的作用,同时也能保持多线程下的语义正确性。

如果是jdk1.5以上,还可采用enum方式来实现,也可避免多线程的问题。

Java代码 复制代码
  1. enum Singleton {      
  2.   INSTANCE;      
  3.   public static Singleton getInstance() {      
  4.     return INSTANCE;      
  5.   }    
  6.   
  7.   public void sss() {   
  8.     System.out.println("sss");   
  9.   }   
  10. }  
enum Singleton {   
  INSTANCE;   
  public static Singleton getInstance() {   
    return INSTANCE;   
  } 

  public void sss() {
    System.out.println("sss");
  }
}



关于反射
以上的写法,除了枚举方式,其他的写法均可用反射得到Constructor来newInstance得到实例。

关于继承
无论是饿汉式,懒汉式写法均不可继承,因此有如下登记式写法。

Java代码 复制代码
  1. public class Sington {   
  2.     private static HashMap<String, Sington> map = new HashMap<String, Sington>();   
  3.   
  4.     protected Sington() {   
  5.     }   
  6.   
  7.     public static synchronized Sington getInstance(String classname) {   
  8.         Sington singleton = (Sington) map.get(classname);   
  9.         if (singleton != null) {   
  10.             return singleton;   
  11.         }   
  12.         try {   
  13.             singleton = (Sington) Class.forName(classname).newInstance();   
  14.         } catch (ClassNotFoundException cnf) {   
  15.   
  16.         } catch (InstantiationException ie) {   
  17.   
  18.         } catch (IllegalAccessException ia) {   
  19.   
  20.         }   
  21.         map.put(classname, singleton);   
  22.         return singleton;   
  23.     }   
  24. }  
public class Sington {
	private static HashMap<String, Sington> map = new HashMap<String, Sington>();

	protected Sington() {
	}

	public static synchronized Sington getInstance(String classname) {
		Sington singleton = (Sington) map.get(classname);
		if (singleton != null) {
			return singleton;
		}
		try {
			singleton = (Sington) Class.forName(classname).newInstance();
		} catch (ClassNotFoundException cnf) {

		} catch (InstantiationException ie) {

		} catch (IllegalAccessException ia) {

		}
		map.put(classname, singleton);
		return singleton;
	}
}


对于子类的

Java代码 复制代码
  1. public class SingtonChild extends Sington {   
  2.     public SingtonChild() {   
  3.     }   
  4.   
  5.     static public SingtonChild getInstance() {   
  6.         return (SingtonChild) Sington   
  7.                 .getInstance("util.SingtonChild");   
  8.     }   
  9.   
  10.     public String about() {   
  11.         return "Hello,I am children.";   
  12.     }   
  13. }  
public class SingtonChild extends Sington {
	public SingtonChild() {
	}

	static public SingtonChild getInstance() {
		return (SingtonChild) Sington
				.getInstance("util.SingtonChild");
	}

	public String about() {
		return "Hello,I am children.";
	}
}



ClassLoader对单例的影响
同样的单例类,由不同的ClassLoader装载就会有多个实例,为了确保我们的单例类只会被同个类ClassLoader加载,我们就需要为它指定一个ClassLoader

Java代码 复制代码
  1. private static Class getClass(String classname)     
  2.                                          throws ClassNotFoundException {    
  3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
  4.       if(classLoader == null)    
  5.          classLoader = Singleton.class.getClassLoader();     
  6.       return (classLoader.loadClass(classname));    
  7.    }    
  8. }   
private static Class getClass(String classname)  
                                         throws ClassNotFoundException { 
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();  
      if(classLoader == null) 
         classLoader = Singleton.class.getClassLoader();  
      return (classLoader.loadClass(classname)); 
   } 
} 



序列化对单例的影响
如果单例类实现了序列化接口,我们序列化一次,然后反序列化多次,会得到多个不同实例。我们通过实现readResolve()来解决这个问题。

Java代码 复制代码
  1. public class Singleton implements Serializable {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {  }    
  5.   
  6.    public static synchronized Singleton getInstance() {    
  7.       if(instance == null) {    
  8.          instance = new Singleton();    
  9.       }    
  10.       return instance;    
  11.    }    
  12.   
  13.    private Object readResolve() {    
  14.         return instance;    
  15.    }   
  16.  }   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值