单例设计模式概述
- 单例设计模式:保证类在内存中只有一个对象。并提供一个访问它的全局访问点。
- 如何保证类在内存中只有一个对象呢?
- 控制类的创建,不让其他类来创建本类的对象。private
- 在本类中定义一个本类的对象。Singleton s;
- 提供公共的访问方式。 public static Singleton getInstance(){return s}
- 单例模式解决的问题:可以保证一个类在内存中的对象的唯一性。必须对于多个程序使用同一个配置信息对象时,就必须保证对象的唯一性。
- 注意事项:
- 不要使用单例模式存取全局变量。这违背了单列模式的用意,最好放到对应类的静态成员中。
- 不要将数据库连接做成单例,因为一个系统可能与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放。
饿汉式(单例设计模式写法) 开发用这种方式。
- 类一加载,就将单例初始化好了,对象想要调用,就要使用getInstance()方法
- 弊端:
- 空间使用率不高
- 类加载时实例化,意味着该类无法在程序运行过程中通过运行参数实例化,代码失去灵活性。
class Singleton {
private Singleton(){}
private static Singleton s = new Singleton();
public static Singleton getInstance() {
return s;
}
public static void print() {
System.out.println("11111111111");
}
}
懒汉式(单例设计模式)单例延迟加载模式
- 延迟加载,只是你要使用的时候才会帮你初始化单例。
- 弊端:
- 容易引发线程问题,因为如果线程1访问getInstance()方法时,被迫陷入沉睡,而线程2来访问的时候并没有单例初始化,所以就会初始化单例,而恰好线程1苏醒,继续初始化单例,从而产生多个单例。
class Singleton {
private Singleton(){}
private static Singleton s;
public static Singleton getInstance() {
if(s == null)
s = new Singleton();
return s;
}
public static void print() {
System.out.println("11111111111");
}
}
双重检查锁(单例设计模式)
- 所谓的双重检查,是在同步前后的两次
if(instance == null)
判断,是否已经存在实例,锁自然指的就是synchronized
关键字。 - 对着代码,我们再来考虑两个线程同时通过了第一道if(instance == null)检查,但因为同步锁是互斥的,只能第一个线程释放后,第二个线程才能持有。这保证了同步代码块的原子性,在同步代码块中的如果instance还为创建,此时才会创建。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
- 静态内部类的特点:
* 线程安全:由静态内部类中的静态成员初始化时创建实例,通过JVM类加载机制来保证线程的安全性。
* 懒加载:使用静态内部类的方式,让类SingletonHolder只有在使用的时候才会被加载,实例才会创建,借机实现了懒加载。
public class Singleton {
private Singleton(){}
private static class SingletonHolder{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
总结
- 单例设计模式作为入门设计模式,还是有许多可以深究的地方,还需要努力学习。
- 参考文献:推荐写的非常详细
单例设计模式详解