单例模式
目的
为什么要使用单例模式?不用重复创建的变量,例如数据库连接、缓存连接等信息。通过单例模式来减少系统的创建变量的开销。
特点
- 单例类只能有一个实例
- 单例类必须是自己创建自己的唯一实例
- 单例类必须给其它所有对象提供徐和谊实例
编码
单例模式分类
单例模式又分为两种:懒汉模式
,饱汉模式
。懒汉模式可以理解只能在其它对象获取该单例对象的时候才实例化,以至于非常饿。饱汉模式相反
懒汉/饱汉模式
1 | /** |
懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例。上述创建的单例虽然是线程安全的,但是每次经过同步块的检查会降低访问速度
创建单例类的双重检查机制
变量上加上volatile
关键字。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class LazySingletonV2 {
// 加上 volatile 关键字使这个变量在多线程的环境下,所有线程都能观察到他的值
private static volatile LazySingletonV2 lazySingletonV2 = null;
private LazySingletonV2() {
}
public static LazySingletonV2 getInstance() {
if (lazySingletonV2 == null) {
synchronized (LazySingletonV2.class) {
if (lazySingletonV2 == null) {
lazySingletonV2 = new LazySingletonV2();
}
}
}
return lazySingletonV2;
}
}
更优方案
上述的两个创建方式中还是不可避免的使用了同步块,在多线程的环境中有没有在保证线程安全的前提下的更优的解决方案?在某些情况下,Jvm霍隐形的执行同步,从而不需要自己来执行同步控制
- 静态初始化器(在静态字段上或者static修饰的初始化)
- final类型字段
- 在创建线程之前创建对象时
- 线程可以看到它将要处理的对象时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Singleton {
private Singleton(){}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}