概述
先从最常见的单例模式开始写起
- 作用:确保某个类只有一个实例
- 优点:
- 提供了对唯一实例的受控访问
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能
- 允许可变数目的实例
- 缺点:
- 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难
- 单例类的职责过重,在一定程度上违背了“单一职责原则”
- 不适合变化的对象
- 适用场景:
- 需要频繁的进行创建和销毁的对象
- 创建对象时耗时过多或耗费资源过多,但又经常用到的对象
- 工具类对象(可考虑使用静态类代替)
- 频繁访问数据库或文件的对象
实现
最简单的实现(饿汉式)
代码简洁而且线程安全,但是缺点是即使不调用getInstance()方法ins也会被初始化,造成一定的资源浪费
public class Singleton {
private static Singleton ins = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return ins;
}
}
懒汉式
为了避免饿汉式的浪费,可按照如下实现
public class Singleton {
private static Singleton ins;
private Singleton() {}
public static Singleton getInstance() {
if (ins == null) {
// 只有调用getInstance()方法后ins才会初始化,但是线程不安全
ins = new Singleton();
}
return ins;
}
}
虽然解决了资源浪费的问题,但是在多线程环境下,上面的代码是不安全的。要想在多线程环境下使用懒汉式可以有如下方式:
同步方法
这个不多说了,直接用synchronized修饰方法,安全但是低效
public class Singleton {
private static Singleton ins;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (ins == null) {
ins = new Singleton();
}
return ins;
}
}
双检锁
public class Singleton {
private static volatile Singleton ins;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (ins == null) {
// 只对初始化ins代码块同步提高效率
synchronized (Singleton.class) {
if (ins == null) {
// 此处if是防止有多个线程同时执行到外层if而产生多个实例
ins = new Singleton();
}
}
}
return ins;
}
}
静态内部类
比起双检锁更优雅的一种实现方式,使用类加载机制(只有在用到某一类时才会加载)保证延迟加载效果,同时类加载器又保证了线程安全
public class Singleton {
private Singleton() {}
public static synchronized Singleton getInstance() {
return SingletonIns.INS;
}
private static class SingletonIns {
private static final Singleton INS = new Singleton();
}
}
枚举
通过枚举也可以实现线程安全的单例模式,但是只有JDK 1.5以后才可以使用
public enum Singleton {
INS;
}
登记式
登记式实际对一组单例模式进行的维护,具体实现是当要获取实例时先读取Map中是否存在该实例,若存在则返回,否则创建实例,存入Map并返回该实例。不是很难就不上代码了,嘿嘿。。。
实际应用
jdk中的Runtime,典型的饿汉式
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
...
}