单例模式
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
结构图
Singleton类,定义一个负责GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
public class Singleton {
private static Singleton instatnce;
private Singleton() { // 无参构造私有化,防止外部new对象
}
// 得到Singleton的实例,唯一的途径
public static Singleton getInstance() {
if (instatnce == null) {
instatnce = new Singleton();
}
return instatnce;
}
}
客户端
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
if (s1 == s2) {
System.out.println("对象相同");
}
多线程
另外还需要注意一些细节,比如,多线程的程序中,多个线程同时访问Singleton类,调用getInstance()方法时,会可能创建多个实例。我们需要加一些锁。
public synchronized static Singleton getInstance() {
if (instatnce == null) {
instatnce = new Singleton();
}
return instatnce;
}
使用synchronized
关键字锁住此方法,使得此方法在同一个时间点只有一个线程能进入访问。
这样看起来也没有问题,但是会有一个性能的问题:每次调用此方法的时候都需要锁,但其实我们只需要在创建instance 的时候让一个线程去创建就行。那么再改造一下。
双重锁定
public class Singleton {
private volatile static Singleton instatnce;
private Singleton() { // 无参构造私有化,防止外部new对象
}
// 得到Singleton的实例,唯一的途径
public static Singleton getInstance() {
if (instatnce == null) {
synchronized (Singleton.class){ // 防止多个线程进入创建实例
if (instatnce == null) {
instatnce = new Singleton();
}
}
}
return instatnce;
}
}
使用volatile
关键字,当synchronized
变量被初始化成Singleton时,多个线程能正确处理synchronized变量。
synchronized
代码块儿中,还需要再加一层对instance的判断,因为当多个线程访问此方法时,它们都是通过了第一层instance==null的判断的,里面再加一层判断,防止后进入的线程又一次去创建实例。
静态初始化
在实际应用中,上面的做法都能应付自如。不过为了确保实例唯一,还是会带来很大的性能代价。对于性能要求很高的程序,建议还是采用静态初始化,也就是饿汉模式。
public class Singleton {
private static Singleton instatnce = new Singleton();
private Singleton() { // 无参构造私有化,防止外部new对象
}
// 得到Singleton的实例,唯一的途径
public static Singleton getInstance() {
return instatnce;
}
}
这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象称之为饿汉式单例类。
要在第一次被引用时,才将自己实例化,则是被称之为懒汉式单例类。
大多数情况都是选择使用饿汉模式单例类,实际按项目需求做出选择。