单例模式饿汉式与懒汉式,内部类实现单例模式

单例模式

单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对持有一个实例,并提供一个全局访问点。

饿汉式单例模式

就是在类加载的时候就立即初始化,并且创建单例对象。绝对的线程安全

public class HungrySingleton {
	//private static final HungrySingleton hungrySingleton = new HungrySingleton();
	
	//静态代码块
	private static final HungrySingleton hungrySingleton;
	static {
		hungrySingleton = new HungrySingleton();
	}
	
	private HungrySingleton() {}
	
	public static HungrySingleton getInstance() {
		return hungrySingleton;
	}
}

由于不加锁,执行效率较高。
但在类加载的时候就已经实例化,不管用不用,都在占用内存,造成资源浪费。

懒汉式单例模式

就是在外部类调用的时候才会初始化。

public class LazySingleton {

	private static LazySingleton lazy = null;
	private LazySingleton() {
	}

		 public static LazySingleton getInstance() {
	 	//单线程安全
	 	if(lazy==null) {
	 		lazy = new LazySingleton();
	 	}
	 	return lazy;
	}
}

该方法仅在单线程下线程安全。
做一点优化:

public synchronized static LazySingleton getInstance() {
		if (lazy == null) {
			lazy = new LazySingleton();
		}
		return lazy;
	}

加synchronized后让方法变为同步方法,实现在多线程下的线程安全,但仍存在问题。
因为每次调用方法获取实例都会进行加锁,如果线程过多就会影响效率。

再做一点优化:

	/**
	 * 双重检查锁的单例模式
	 * @return
	 */
	public static LazySingleton getInstance() {
		if(lazy==null) {
			synchronized(LazySingleton.class) {
				if(lazy==null) {
					lazy=new LazySingleton();
				}
			}
		}
		return lazy;
	}

采用双重判断并加锁机制来实现多线程下线程安全。

但这样还是使用了synchronized关键字,总归要上锁,对程序性能还是存在一定的影响。

用内部类来实现单例模式

	public class LazyInnerClassSingleton {
	
	private LazyInnerClassSingleton() {}
	
	public static final LazyInnerClassSingleton getInstance() {
		//再返回结果之前,一定会先加载内部类
		return LazyHolder.LAZY;
	}
	
	//默认不加载
	private static class LazyHolder{
		private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
	}
}

这个形式兼顾了饿汉式模式的内存浪费问题和synchronized的性能问题。

这样就完美了吗?

当然不是,单例模式的构造函数除了加private关键字外,没有其他任何处理,尝试使用反射来调用其构造函数。

public static void main(String[] args) {
		try {
			Class<?> clazz = LazyInnerClassSingleton.class;
			
			//通过反射获取私有的构造方法
			Constructor<?> c = clazz.getDeclaredConstructor(null);
			
			//强制访问
			c.setAccessible(true);
			
			//暴力初始化
			Object o1 = c.newInstance();
			Object o2 = c.newInstance();
			System.out.println(o1==o2);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

结果打印为false,这并不是单例模式希望看到的,对原来的构造函数再做一些限制,一旦出现多次重复创建,则直接抛出异常。

	private LazyInnerClassSingleton() {
		if(LazyHolder.LAZY!=null) {
			throw new RuntimeException("不允许创建多个实例");
		}
	}
匿名

发表评论

匿名网友