一、单例特性
1:单例类只能有一个实例
2:单例类必须自己创建自己的实例类(其他类无法创建)
3:单例类必须给所有其他对象提供这一实例
二、使用场景
意图:保证一个类仅有一个实例,并提供一个全局访问的访问点(获取实例的方法)
解决:一个全局使用的类频繁创建和销毁
使用时机:控制实例数目,节省系统资源,业务上对象的性质
逻辑:判断是否存在该类实例,有则返回,无则创建
代码方式:构造函数私有化
三、优缺点
1、优点
1)内存中只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
2)避免文件的多重占用(比如写文件操作)
2、缺点
1)没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎样来实例化
四、实现方式
1)懒汉式:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
特点:Lazy、线程安全、创建简单
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
2)饿汉式:
特点:线程安全、创建简单
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
3)双检锁/双重校验锁(DCL,即 Double-checked Locking)
特点:Lazy、线程安全、较复杂
优点:双锁机制,在多线程情况下既安全又性能高,适用于实例域需要延迟初始化的情况
4)登记式/静态内部类
特点:Lazy、线程安全、一般难度
优点:双检锁功能一致,适用于静态域需要延迟初始化的情况
5)枚举
特点:线程安全、创建简单
优点:简单、自动支持序列化、防止多次实例化、不能通过反射来调用私有方法
五、代码
import lombok.Data;
/**
* 懒汉式(线程安全)
*/
public class Singleton {
// 静态的私有的实例
private static Singleton instance;
//重要--无参构造私有化,不写默认public级别的构造参数
private Singleton(){}
// synchronized 加锁保证单例,影响效率
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
/**
* 饿汉式(线程安全)
*/
class Singleton2{
// 类加载是就初始化好单一对象
private static Singleton2 instance = new Singleton2();
// 重要--无参构造私有化,不写默认public级别的构造参数
private Singleton2(){}
// instance方法
public static Singleton2 getInstance(){
return instance;
}
}
/**
* 双检锁(安全高性能)
*/
class DoubleLock{
// volatile 开启jvm禁止重排序的问题(重排序导致数据读取不正确)
private volatile static DoubleLock instance;
// 重要--无参构造私有化
private DoubleLock(){}
// 双检锁
public static DoubleLock getInstance(){
// 多个人同时进来 (n人进入)
if(instance == null){
// 多人排队,获取锁的先来(n-1人排队)
synchronized (DoubleLock.class){
// 获取锁的来获取实例,没有就创建,有就直接返回(第1人实例化对象,n-1人直接获取第1人创建的对象)
if(instance == null){
instance = new DoubleLock();
}
}
}
return instance;
}
}
/**
* 静态内部类(登记式)
*/
class Singleton3{
// 静态内部类
private static class SingletonInner{
private static final Singleton3 INSTANCE = new Singleton3();
}
// 重要--无参构造私有化
private Singleton3(){};
// 直接返回静态内部类的静态资源
public static final Singleton3 getInstance(){
return SingletonInner.INSTANCE;
}
}
/**
* 枚举单例
*/
enum SingletonEnum{
INSTANCE(1,"测试实列");
private Integer code;
private String msg;
SingletonEnum(Integer code, String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void whateverMethod() {
}
}