单例模式有饿汉模式和懒汉模式
饿汉模式
第一步,要把这个默认的构造方法改成私有的,也就是在其他地方不能创建实例
第二步,提前创建出来一个实例,并进行初始化
第三步,提供一个方法来获取这个实例
public class Singleton {
//构造方法私有化
private Singleton(){}
//创建一个实例进行初始化
//static类方法,只能访问类成员,类变量
private static Singleton instance = new Singleton();
//其他类只能通过getInstance来获取这个实例
public static Singleton getInstance(){
//如果这儿不是static,这个方法就是实例方法或者对象方法,只能通过实例去调用
return instance;
}
}
懒汉模式
1.先把构造方法改成私有的
2.创建一个实例不进行初始化
3.对外提供一个方法来获取这个实例,并进行初始化
当外部调用这个方法时,肯定是需要用到这个实例,这时候再去进行初始化
//构造函数私有化
private Singleton2(){}
//创建实例,不进行初始化
private static Singleton2 instance = null;
//对外提供一个方法,获取该实例,并进行初始化
public static Singleton2 getInstance(){
//instance = new Singleton2();//只在第一次调用的时候初始化
if(instance == null){ //读操作
instance = new Singleton2(); //写操作
}
return instance;
}
如果是两个线程同时调用
那么两个线程都要对其进行初始化,所以要进行加锁
要满足单例模式,加锁的对象一定是唯一的
也就是一个程序里只有一个对象,才能保证多个线程访问时,对同一个对象进行加锁
只需要第一个线程对该实例进行初始化,后面的线程,只需要直接获取前面这个实例就可以
//构造函数私有化
private Singleton2(){}
//创建实例,不进行初始化
private static Singleton2 instance = null;
//对外提供一个方法,获取该实例,并进行初始化
public static Singleton2 getInstance() {
//instance = new Singleton2();//只在第一次调用的时候初始化
synchronized (Singleton2.class) { //类对象,一个程序只有一个
if (instance == null) { //读操作
instance = new Singleton2(); //写操作
}
return instance;
}
}
但是只要线程调用getInstance就会进行加锁,效率很低,所以修改代码为instance==null的时候在进行加锁
//构造函数私有化
private Singleton2(){}
//创建实例,不进行初始化
private static volatile Singleton2 instance = null;//禁止指令重排序
//对外提供一个方法,获取该实例,并进行初始化
public static Singleton2 getInstance() {
//instance = new Singleton2();//只在第一次调用的时候初始化
if (instance == null) { //判断是否加锁
synchronized (Singleton2.class) { //类对象,一个程序只有一个
if (instance == null) { //读操作
instance = new Singleton2(); //写操作
}
}
}
return instance;
}
}