首页 🔴 Java

单例模式

单例模式是 Java 中最简单的设计模式之一, 这种类型的设计模式属于创建型模式, 它提供了一种创建对象的最佳方式

这种模式涉及到一个单一的类, 该类负责创建自己的对象, 同时确保只有单个对象被创建, 这个类提供了一种访问其唯一的对象的方式, 可以直接访问, 不需要实例化该类的对象

注意:

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其他对象提供这一实例

介绍

意图:

保证一个类仅有一个实例, 并提供一个访问它的全局访问点

主要解决:

一个全局使用的类频繁地创建与销毁

何时使用:

当您想控制实例数目, 节省系统资源的时候

如何解决:

判断系统是否已经有这个单例, 如果有则返回, 如果没有则创建

关键代码:

构造函数是私有的

应用实例:

  • 一个班级只有一个班主任
  • Windows 是多进程多线程的, 在操作一个文件的时候, 就不可避免地出现多个进程或线程同时操作一个文件的现象, 所以所有文件的处理必须通过唯一的实例来进行
  • 一些设备管理器常常设计为单例模式, 如一个电脑有两台打印机, 在输出的时候就要处理不能两台打印机打印同一个文件

优点:

  • 在内存里只有一个实例, 减少了内存的开销, 尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)
  • 避免对资源的多重占用(比如写文件操作)

缺点:

  • 没有接口, 不能继承, 与单一职责原则冲突, 一个类应该只关心内部逻辑, 而不关心外面怎么样来实例化

使用场景:

  • 要求生产唯一序列号
  • WEB 中的计数器, 不用每次刷新都在数据库里加一次, 用单例先缓存起来
  • 创建的一个对象需要消耗的资源过多, 比如 I/O 与数据库的连接等

单例模式的几种实现方式

方式一

饿汉式

是否 Lazy 初始化: 否
是否多线程安全: 是
实现难度: 易
优点: 没有加锁, 执行效率会提高
缺点: 类加载时就初始化, 浪费内存

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

方式二

懒汉式(线程不安全)

是否 Lazy 初始化: 是
是否多线程安全: 否
实现难度: 易
优点: 实现简单, 没有加锁
缺点: 线程不安全, 多个线程同时操作容易重复创建对象

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  

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

方式三

懒汉式(线程安全)

是否 Lazy 初始化: 是
是否多线程安全: 是
实现难度: 易
优点: 实现简单, 线程安全
缺点: 直接对方法加锁, 效率太低

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

方式四

双检锁/双重校验锁(DCL, 即 double-checked locking)

是否 Lazy 初始化: 是
是否多线程安全: 是
实现难度: 难
优点: 线程安全, 效率相对较高
缺点: 代码实现起来较前面几个复杂

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

方式五

枚举

使用枚举可以避免线程不安全, 并且可以避免反射乱搞

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



文章评论