设计模式——单例模式

158 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

单例模式

特点

  • 只有一个实例
  • 提供了一种访问其唯一的对象的方式
  • 构造函数是私有的
  • 自己创建唯一实例

使用场景

  • 其对象是唯一的,比如唯一的男/女朋友,唯一的主席,唯一的班主任
  • 在多线程中为了防止多个线程访问同一个资源文件,所以访问文件的对象是单例的

实现逻辑

  1. 构造方法都是私有的
  2. 有一个方法供获取对象getInstance()
  3. new对象之前需要判断对象是否存在,存在则直接返回存在的对象,不存在才进行new

实现代码

public class SingleObject{
    private SignleObject singleton;
    
    private SignleObject(){
        //1. 构造方法私有
    }
    
    //2. 一个方法获取对象
    public static SignleObject getInstance(){
        //3. 不存在才new
        if(singleton==null){
            singleton=new SignleObject();
            return singleton;
        }else{
            return singleton;
        }
    }
    
    public void sayMe(){
        System.out.print("singleObject")
    }
}

方法调用

public class SingletonPatternDemo {
   public static void main(String[] args) {
 
      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();
 
      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();
 
      //显示消息
      object.showMessage();
   }
}

几种实现方式及特点

懒汉式 线程不安全

懒汉式:私有静态变量只有在第一次用的时候才会加载,不用就不会加载

线程不安全:多个线程同时通过了if (singleton == null)的判断逻辑,从而导致生成了多个对象

public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式 线程安全

即针对getInstance方法加锁,即同一时间进入该方法的只有一个线程

public static synchronized Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}

饿汉式

直接加载一个对象,这样就不会出现线程不安全的情况了

private static Singleton instance = new Singleton();

双检锁/双重校验锁(DCL:double checked locking)

相对于懒汉式线程安全增加了

  1. 先判断object不存在再加锁
  2. 锁内再判断一次

相对于懒汉式线程安全的情况,先进行object==null的判断再加锁.可以提升并发行,因为懒汉式先加锁再判断如果!=null的时候也会进行加锁

而双层判断,防止都通过第一层判断加锁了之后就一定会生成两个对象

public static Signleton getInstance(){
    if(signleton == null){
        synchronized (Signleton.class){
            if(signleton == null){
                signleton == new Signleton();
            }
        }
    }
}

登记式/静态内部类(未理解)

Loading...

只有当调用 getInstance() 方法从而触发 SingletonHolder.INSTANCESingletonHolder 才会被加载 ,JVM 能确保 INSTANCE 只被实例化一次.

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

枚举实现(未理解)

Loading... 通过反射机制,是可以实例化构造方法为private类的. 枚举实现自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

只能说更安全