1、单例模式概述
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。——百度百科
2、单例模式分类及特点
注意:一般的单例在序列化和反射攻击的情境下会被破坏单例,即可能生成多个实例,也可以用其他方法解决,以静态内部类为例
防止反射攻击方法:
private StaticInner(){
//构造器判断,防止反射攻击
if(InnerInstance.STATIC_INNER != null){
throw new IllegalStateException();
}
}
序列化安全方法:
private Object readResolve() throws ObjectStreamException {
return InnerInstance.STATIC_INNER;
}
但是枚举类型天生是序列化和反射攻击安全的。
3、各种类型实现
1、饿汉模式
/**
* 饿汉模式
* 优点:线程安全,调用时效率高
* 缺点:不管是否使用都创建对象,可能造成资源浪费
*/
package singleton;
public class Hungry {
private Hungry(){}
private static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
2、懒汉模式
/**
* 非线程安全的懒汉模式
* 优点:调用时才创建对象,不会浪费资源
* 缺点:非线程安全
*/
package singleton;
public class LazyNotSafe {
private LazyNotSafe(){}
private static LazyNotSafe lazyNotSafe;
public static LazyNotSafe getInstance(){
if(lazyNotSafe == null){
lazyNotSafe = new LazyNotSafe();
}
return lazyNotSafe;
}
}
3、加锁懒汉模式
/**
* 线程安全的懒汉模式
* 优点:线程安全
* 缺点:方法锁,效率较低
*/
package singleton;
public class LazySync{
private LazySync(){}
private static LazySync lazySync;
public static synchronized LazySync getInstance(){
if(lazySync == null){
lazySync = new LazySync();
}
return lazySync;
}
}
4、双重检查锁
/**
* 双重检查锁懒汉模式,线程安全,效率较高
* 优点:线程安全,效率较高
* 注意:lazyDoubleLock对象需要加volatile关键字,禁止JVM指令重排,否则可能导致返回未创建好的对象
*/
package singleton;
public class LazyDoubleLock {
private LazyDoubleLock(){}
private static volatile LazyDoubleLock lazyDoubleLock;
public static LazyDoubleLock getInstance(){
if(lazyDoubleLock == null){
synchronized (LazyDoubleLock.class){
if(lazyDoubleLock == null){
lazyDoubleLock = new LazyDoubleLock();
}
}
}
return lazyDoubleLock;
}
}
5、静态内部类
/**
* 静态内部类
* 优点:线程安全,效率较高,
* 缺点:
*/
package singleton;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class StaticInner implements Serializable {
private StaticInner(){
//构造器判断,防止反射攻击
if(InnerInstance.STATIC_INNER != null){
throw new IllegalStateException();
}
}
private Object readResolve() throws ObjectStreamException {
return InnerInstance.STATIC_INNER;
}
public static StaticInner getInstance(){
return InnerInstance.STATIC_INNER;
}
private static class InnerInstance{
private static final StaticInner STATIC_INNER = new StaticInner();
}
}
6、枚举
/**
* 枚举
* 优点:都是
* 缺点:写起来不习惯
*/
package singleton;
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
总结:
如果对空间要求不高,可以用饿汉模式;
懒汉模式由于非线程安全,带锁懒汉模式由于效率太低,不建议使用;
一般来说,双重检查锁是用得较多的懒汉模式;
静态内部类效果上跟懒汉模式差不多;
枚举不管从代码量还是功能上讲,都是目前最推崇的单例模式,只是用习惯的人不多。