1.单例模式介绍
单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式优点:
1.一个类只有一个实例,系统资源开销小
2.优化共享资源的访问
常见单例模式分类:
主要:
饿汉式(线程安全,调用效率高,但是不能延时加载)
懒汉式(线程安全,调用效率低,但是能延时加载)
其他:
枚举单例(线程安全,调用效率高,但不能延时加载)
静态内部类式(线程安全,调用效率高,且能够延时加载)
双重检测锁式(线程安全,调用效率高,且能够延时加载)
2.单例模式实例
1.饿汉式
package com.lyf.Singleton;
/**
* 饿汉模式,不管用不用的到对象,都会实例化一个。
* 所以不能延时加载
* 私有化构造方法,并且使用静态变量初始化对象,确保一个类只有一个实例
*/
public class HungryMode {
/**
* 1.定义静态变量,当类加载的时候会初始化一个静态实例
*/
private static HungryMode hungryMode = new HungryMode();
/**
* 私有化构造方法,不允许外部直接实例化
*/
private HungryMode(){
}
/**
* 对外开放获取实例的方法
* @return
*/
public static HungryMode getInstance(){
return hungryMode;
}
}
2.懒汉式
package com.lyf.Singleton;
public class LazyMode {
/**
* 定义一个静态变量,但是不初始化对象,需要的时候再初始化
*/
private static LazyMode lazyMode;
/**
* 私有化构造方法,不允许外部直接实例化
*/
private LazyMode(){
}
/**
* 对外提供一个获取实例的方法
* 使用synchronized能够确保高并发时只有一个线程能够进来,也因为导致调用效率不高
* 可以延时加载,等需要的时候再初始化
* @return
*/
public synchronized static LazyMode getInstance(){
if (lazyMode == null){
lazyMode = new LazyMode();
}
return lazyMode;
}
}
3.枚举单例模式
具体有关枚举的详细内容可以看Java使用枚举实现单例模式
package com.lyf.Singleton;
/**
* 枚举单例,枚举本身就是单例的,
*/
public enum EnumMode {
/**
* 定义了一个枚举元素,相当于单例的实例对象了
* 相当于private static final EnumMode ENUMMODE = new EnumMode()
* 所以不能延时加载,有点类似饿汉式
*/
ENUMMODE;
/**
* 定义对枚举的操作,可以通过EnumMode.ENUMMODE.operation()来调用
*/
public void operation(){
}
}
4.静态内部类模式
关于静态类、非静态类和静态内部类的何时初始化可以看静态内部类何时初始化
package com.lyf.Singleton;
/**
* 静态内部类
*/
public class StatisInternalMode {
/**
* 私有化构造方法
*/
private StatisInternalMode(){
}
/**
* 定义一个静态内部类,静态内部类中定义了final类型的静态变量,实例化StatisInternalMode对象
* 初始化StatisInternalMode的时候不会初始化静态内部类StatisClassInstance
* 静态内部类只有在调用的时候才会被初始化,所以实现了延时加载
* 且因为final的原因,线程安全
*/
private static class StatisClassInstance{
private static final StatisInternalMode statisInternalMode = new StatisInternalMode();
}
/**
* 对外提供获取实例化对象的方法
* @return
*/
public static StatisInternalMode getInstance(){
return StatisClassInstance.statisInternalMode;
}
}
5.双重检测锁式
使用双重检测锁式的单例模式最主要的一点在定义静态变量的时候需要添加volatile字段禁止指令重排序,否则会引发问题。
有关为何需要添加volatile字段的理由可以看双重检查的缺陷以及更好的单例模式
package com.lyf.Singleton;
/**
* 双重检测法
* 高效且延时加载
*/
public class DoubleDetectionMode {
/**
* 这里必须使用volatile修饰禁止指令重排序
* 因为jvm在执行的时候会对指令进行重排序
* 例如new DoubleDetectionMode()
* 对于JVM而言,会分为三个步骤
* 1)分配空间给对象
* 2)在空间内创建对象
* 3)将对象赋值为引用
* 因为步骤2和3的执行顺序的改变不会影响最终的结果,所以可能会导致步骤3先执行,2后执行
* 而多线程访问的情况下,会出现上一个线程获取同步锁开始初始化对象的时候,jvm先执行步骤3,而后另一个线程进来
* 判断doubleDetectionMode == null 的结果为false,从而获取到一个不安全对象
*/
volatile private static DoubleDetectionMode doubleDetectionMode;
/**
* 私有化构造方法
*/
private DoubleDetectionMode(){
}
/**
* 对外提供获取实例对象的方法
* 只有当对象不存在的时候才会进行对象的实例化
* 且在进行对象实例化之前加了同步锁,确保只有一个线程可以进入,从而实现了线程安全
* @return
*/
public static DoubleDetectionMode getInstance(){
if (doubleDetectionMode == null){
synchronized (DoubleDetectionMode.class){
if (doubleDetectionMode == null){
doubleDetectionMode = new DoubleDetectionMode();
}
}
}
return doubleDetectionMode;
}
}
3.总结
对于常见的单例模式的实现大致就以上5种,若其中有任何错误欢迎指正!