单例模式
什么是单例设计模式?
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
类结构图
具体实现
需要:
(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
(2)在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。
(3)定义一个静态方法返回这个唯一对象。
单例模式的几种实现方式
饿汉模式/立即加载
饿汉模式就是在类的初始化的时候已经将对象创建完毕,不管对象是不是立刻会用到,所以称之为饿汉式。
public class Singleton{
//构造方法私有化
private Singleton(){};
//将自身实例对象设置为一个属性,并用static,final修饰(final修饰保证它是不会被改变的,)
private static final Singleton instance = new Singleton();
//静态方法返回实例
public static Singleton getInstance(){
return instance;
}
}
“饿汉模式”的优缺点:
优点:实现起来简单,在多线程中不会出现线程安全问题。
缺点:当类被初始换的时候,会初始化static的instance,静态变量被创建并被分配内存空间,无论是否使用这个实例,直到jvm回收该类之前,静态变量都存在于内存中,因此在某些特定时间下会浪费内存。
方式二:懒汉模式/延迟加载
懒汉模式就是为了优化饿汉式带来的内存浪费,当需要创建对象时才创建。
public class Singleton{
//构造方法私有化
private Singleton(){};
// 将自身实例化对象设置为一个属性,并用static修饰
private static Singleton instance;
//静态方法返回实例
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
“懒汉模式”的优缺点:
优点:实现起来简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,某些特定时间下会节约内存
缺点:在多线程环境中,这种实现方法会有线程安全的问题,会出现多个实例的情况,无法保证在任何时候都是单例的状态。
方式三:线程安全的“懒汉模式”
public class Singleton{
//构造方法私有化
private Singleton(){};
// 将自身实例化对象设置为一个属性,并用static修饰
private static Singleton instance;
//静态方法返回实例
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
线程安全的“懒汉模式”的优缺点:
优点:实现起来简单。
缺点:,在jdk1.6之后synchronized关键字进行了优化,但是对性能还是有影响。当使用synchronized 放在静态方法上时,整个类都被锁定了,效率低下。
方式四:DCL双检查锁机制(DCL:double checked locking)
public class Singleton{
//构造方法私有化
private Singleton(){};
// 将自身实例化对象设置为一个属性,并用static修饰
//CPU执行时候转化成JVM指令执行
//1.分配内存给这个对象
//2.初始化对象
//3.将分配好的对象和内存地址关联,赋值
//4.用户访问
//可能会出现指令重排
private volatile static Singleton instance;
//静态方法返回实例
public static synchronized Singleton getInstance(){
//如果第一次检查instance,没有进入if中
if(instance == null){
synchronized(Singleton.class){
//某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
DCL的优缺点:
优点:内存占用率高,效率高,线程安全,多线程操作原子性。
方式五:内部类实现(Inner)
public class Singleton{
private Singleton() {
//判断为null,,抛出异常
if(SingletonHolder.instance==null){
throw new RuntimeException("不允许构建多个实例");
}
};
private static class SingletonHolder{
private static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
Inner的优缺点:
优点:巧妙的利用了内部类的特点(类的静态属性只有在第一次加载类的时候才会被初始化),完美的避免了线程安全的问题,性能优越。
缺点:构造方法虽然私有了,,但是反射依然能获得私有的构造方法。
方式六:枚举法(enum)
public class User{
//私有化构造函数
private User(){}
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstance(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
总结:
任何一种设计模式都有优点和缺点,,单例模式也不例外:
优点: 1.在内存中只有一个实例,,减少了内存开销。
2.可以避免对资源的多重占用。
3.设置全局访问点,严格控制访问。
缺点: 如果要扩展单例对象,,只能修改代码。