单例模式,顾名思义,就是一个类只能创建一个实例,并提供一个全局访问点。
该怎么实现单例模式那?
第一步:在JAVA面向对象的语言中,要想创建一个实例对象,只需new Object()即可,但正常情况下我们可以新建许多个实例对象。实例化一个对象时其实是实例化其构造方法,构造方法默认作用域是public的,这个导致该对象可以被外部实例化而没有限制,若该类构造方法的作用域改成private,我们只能在该类内部来新建实例对象。
第二步:既然第一步确保了新建实例对象是自己可控后,怎么确保只能创建一个实例对象?由于外部对象不能实例化该对象,我们需要提供一个静态的获取实例的方法供外部调用,可以在该方法中控制只能实例化一次。
一.懒汉式单例模式
1.1 只有在调用类的实例化方法时,才创建实例对象,线程不安全。
public class Singleton {
// 利用一个静态变量来记录Singleton的唯一实例
private static Singleton uniqueInstance;
// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
private Singleton() {
}
// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
通过多线程环境测试是否只产生一个实例:
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Singleton.getInstance());
}
}).start();
}
}
结果显示并不能保证线程安全,不一定只产生一个实例:
1.2 为了保证线程安全,在牺牲性能的情况下,通过getInstance方法加同步
public class Singleton {
// 利用一个静态变量来记录Singleton的唯一实例
private static Singleton uniqueInstance;
// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
private Singleton() {
}
// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
// 为了保证线程安全,加了同步代码块
public synchronized static Singleton getInstance2() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
结果显示能保证线程安全,只产生一个实例:
1.3 以上方法虽然保证只产生一个实例,但性能差,于是改成同步代码块并加双重锁判断
public class Singleton {
// 利用一个静态变量来记录Singleton的唯一实例
private static Singleton uniqueInstance;
// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
private Singleton() {
}
// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
// 同步代码块并加双重锁判断,即保证线程安全,性能也不会差
public static Singleton getInstance3() {
if (uniqueInstance == null) {
synchronized (Singleton.class)
{
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
二.饿汗式单例模式
在类加载时,就获取到类的实例,能保证线程安全,但不管是否需要,都会获取类的实例,影响性能。
public class Singleton {
// 利用一个静态变量来记录Singleton的唯一实例,加载类时就获取
private static Singleton uniqueInstance = new Singleton();
// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
private Singleton() {
}
// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
// 恶汉式
public static Singleton getInstance4() {
return uniqueInstance;
}
}
三.静态内部类单例模式
首先利用jvm的加载机制保证只能产生一个实例,其次静态内部类也是一个单独的class文件,只有在使用时才加载,保证了效率。书中并没提及该方法,通过网上查询资料找的。
public class Singleton {
// 利用静态内部类来记录Singleton的唯一实例
private static class SingletonHolder {
private static Singleton uniqueInstance = new Singleton();
}
// 把构造器声明为私有,只有来自Singleton类内才可以调用构造器
private Singleton() {
}
// 提供公共的静态实例方法供外部调用,并返回Singleton类的唯一实例
public static Singleton getInstance5() {
return SingletonHolder.uniqueInstance;
}
}
总结: 使用单例模式,为了兼顾线程安全与性能,推荐使用同步代码块并加双重锁判断的懒汉式或静态内部类的形式。
参考:https://blog.csdn.net/twocold_2010/article/details/53241056