一、饿汉式
1. 简单的饿汉式
public class HungrySingleton {
private HungrySingleton(){
}
private final static HungrySingleton h = new HungrySingleton();
public static HungrySingleton getInstance(){
return HungrySingleton.h;
}
}
2. 静态代码块写法
public class HungrySingletonStatic {
private HungrySingletonStatic(){
}
private final static HungrySingletonStatic h;
static {
h = new HungrySingletonStatic();
}
public static HungrySingletonStatic getInstance(){
return HungrySingletonStatic.h;
}
}
3. 两种写法的总结
饿汉式的这两种写法都是一样的,在类初始化的时候就创建了一个对象实例,当调用这个静态方法的时候返回的也是同一个对象实例,符合单例的设计思想。但是如果使用的单例比较多的情况下,使用这种方法在加载类的时候就会耗费很多资源,有些单例类可能暂时用不上,造成资源浪费。
二、懒汉式
1. 简单的懒汉式
public class LazySingleton {
private LazySingleton(){}
private static LazySingleton lazySingleton ;
public static LazySingleton getInstance(){
if(lazySingleton == null){
return new LazySingleton();
}
return lazySingleton;
}
}
这里的懒汉式是线程不安全的,如果要线程安全需要枷锁,加锁之后代码如下
public class LazySingleton {
private LazySingleton(){}
private static LazySingleton lazySingleton ;
public static synchronized LazySingleton getInstance(){
if(lazySingleton == null){
return new LazySingleton();
}
return lazySingleton;
}
}
但是这种懒汉式加锁之后有性能问题
2. 双重检查锁
public class LazySingletonLock {
private LazySingletonLock(){
}
private static LazySingletonLock lazySingleton ;
public static LazySingletonLock getInstance(){
if(lazySingleton == null){
synchronized (LazySingletonLock.class){
if (lazySingleton == null){
lazySingleton = new LazySingletonLock();
}
}
}
return lazySingleton;
}
}
双重检查锁解决了性能问题,但是代码量稍大。
3. 静态内部类
public class LazySingletonStatic {
private LazySingletonStatic(){
}
private static class LazySingleton{
private static final LazySingletonStatic l = new LazySingletonStatic();
}
public static LazySingletonStatic getInstance(){
return LazySingleton.l;
}
}
这里用了Java的类加载机制,静态内部类只有使用的时候才会被加载,但是使用反射强制获取构造方法的时候就会破环单例,使用反射获得的构造方法可以创建无数个实例。
三、注册式
1. 枚举
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getObj(){return data;}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
如果使用反射强制获取枚举的构造方法并且创建实例的时候,枚举的构造方法会抛出一个异常
在加载的时候就会实例化,内存浪费,又变成饿汉式。
2. 容器式
public class Container {
private Container(){}
//hashMap在初始化的时候,会有多个KEY相同,但是值不相同的数据,存储时一个KEY可能存入两个value的情况
private static Map<String,Object> singleton = new HashMap<>();
public static void putInstance(String key,Object object){
if( key.length() > 0 && object != null){
if(!Container.singleton.containsKey(key)){
singleton.put(key,object);
}
}
}
public static Object getInstance(String key){
return singleton.get(key);
}
}
是枚举单例的优化版本,解决了可能造成内存浪费的问题,但是引发了线程安全问题,Spring IOC容器使用的这种方式。