单例模式(Singleton Pattern)
一:概念:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。是创建类模式。
二: 要点:
1、私有的构造方法,且单例类只能有一个实例
这是最基本的,真正做到整个系统中唯一并不容易,通常还要考虑反射破坏、序列化/反序列化、对象垃圾回收等问题。
2、单例类必须自己创建自己的唯一实例,即为指向自己实例的私有静态引用。
3、单例类必须给所有其他对象提供这一实例,以自己实例为返回值的静态的公有的方法。
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例是在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
饿汉式单例
public class Singleton {//饿汉模式
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
优缺点:线程安全,无法实现实例懒加载策略。 是基于JVM类加载机制进行加载,避免了多线性的同步问题,保证只加载一次,所以是线程安全的。
懒汉式单例
public class Single2 {
private static Single2 s = null;
private Single2() { }
public static Single2 getInstance() {
if (s == null)
s = new Single2();
return s;
}
}
优缺点: 懒汉式和饿汉式相比的区别就是懒汉式创建了延迟对象同时饿汉式的实例对象是被修饰为final类型。
- 优点:懒汉式的好处是显而易见的,它尽最大可能节省了内存空间。
- 缺点:在多线程编程中,使用懒汉式可能会造成类的对象在内存中不唯一,
- 虽然通过修改代码可以改正这些问题,但是效率却又降低了。
- 特点:线程不安全,实现了实例懒加载策略。
全局锁式
public class Single3 {
private static Single3 single3;
private Single3() {}
public synchronized static Single3 getInstance() {
if (single3 == null)
single3 = new Single3();
return single3;
}
}
特点:线程安全,且实现了懒加载策略,但是线程同步时效率不高。
静态代码块式
public class Single4 {
private final static Single4 singleton4;
private Single4() { }
static {
singleton4 = new Single4();
}
public static Single4 getInstance() {
return singleton4;
}
}
特点:线程安全,类主动加载时才初始化实例,实现了懒加载策略,且线程安全。
双重校验锁式
public class Single5 {
private static volatile Single5 singleton5;
private Single5() {}
public static Single5 getInstance() {
if (singleton5 == null) {
synchronized (Single5.class) {
if (singleton5 == null) {
singleton5 = new Single5();
}
}
}
return singleton5;
}
}
特点:线程安全,且实现了懒加载策略,同时保证了线程同步时的效率。
但是volatile强制当前线程每次读操作进行时,保证所有其他的线程的写操作已完成。
volatile使得JVM内部的编译器舍弃了编译时优化,对于性能有一定的影响。
静态内部类式【推荐】
public class Single6 {
private Single6() {}
private static class SingletonHolder {
private static final Single6 INSTANCE = new Single6();
}
public static Single6 getInstance() {
return Single6.SingletonHolder.INSTANCE;
}
}
特点:线程安全,不存在线程同步问题,且单例对象在程序第一次 getInstance() 时主动加载 SingletonHolder 和其 静态成员 INSTANCE,因而实现了懒加载策略。
枚举方式
public class Single7 {
private Single7() {}
enum SingletonEnum {
INSTANCE;
private final Single7 singleton7;
private SingletonEnum() {
singleton7 = new Single7();
}
}
public static Single7 getInstance() {
return SingletonEnum.INSTANCE.singleton7;
}
}
特点:线程安全,不存在线程同步问题,且单例对象在枚举类型 INSTANCE
- 第一次引用时通过枚举的 构造函数 初始化,因而实现了懒加载策略。
- 这种方式是Effective Java作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
三、单例模式的优缺点
1、在内存中只有一个对象,节省内存空间。
2、避免频繁的创建销毁对象,可以提高性能。
3、避免对共享资源的多重占用。
4、可以全局访问。
适用情况:
1、需要频繁实例化然后销毁的对象。
2、创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3、有状态的工具类对象。
4、频繁访问数据库或文件的对象。
5、所有要求只有一个对象的场景。
注意事项:
只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。不要做断开单例类对象与类中静态引用的危险操作。多线程使用单例使用共享资源时,注意线程安全问题。
四、小结:
(1)、单例模式可以在一些应用场景带来很好的效果,但不能滥用,因为单例模式并不是一种很好的模式。
(2)、单例模式有多种实现方式,没有特殊要求的,用最基本、最简单的饿汉式,需要延时加载的使用静态内部类方式,需要高安全性的可以使用枚举方式。
(3)、对其他关注点应有所了解,有时间可以深入探究,扩展知识面。