饿汉模式
由于单例模式中,一个类只能拥有一个实例对象,所以需要将类构造方法封装,防止类被创建多个实例对象,但是在使用该类时必须要得到该类的实例对象,因此我们得创建一个获取该唯一实例对象的方法getInstance。
而对于该类的实例对象,在类中我们可以使用属于类的成员变量来保存(即static成员变量)。
//单例模式 - 饿汉模式
class HungrySingleton {
//1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
private static final HungrySingleton instance = new HungrySingleton();
//2.封装构造方法,防止该类被实例出新的对象
private HungrySingleton() {}
//3.获取该类的唯一实例对象
public HungrySingleton getInstance() {
return instance;
}
}
懒汉模式
线程不安全
懒汉模式相比于饿汉模式,区别就是实例对象创建时机不同,懒汉模式需要等到第一次使用时才创建实例对象,所以仅仅只需要修改获取对象的方法即可。
不考虑多线程情况,懒汉模式实现代码如下:
//单例模式 - 懒汉模式
class SlackerSingleton {
//1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
//懒汉单例模式是在使用的时候创建对象,因此初始时对象不应该被创建
private static SlackerSingleton instance;
//2.封装构造方法,防止该类被实例出新的对象
private SlackerSingleton() {}
//3.获取该类的唯一对象,如果没有就创建
public SlackerSingleton getInstance() {
if (instance == null) {
instance = new SlackerSingleton();
}
return instance;
}
}
线程安全
public class LazyHungry {
private LazyHungry(){
System.out.println(Thread.currentThread().getName()+"ok");
}
//对象用的时候才去加载
private static LazyHungry lazyHungry;
public static LazyHungry getInstance()
{
//需要加锁
if(lazyHungry==null){
synchronized (LazyHungry.class){
if(lazyHungry==null){
lazyHungry=new LazyHungry();//不是原子性操作,可能会发生指令重排
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*
* 就有可能出现指令重排问题
* 比如执行的顺序是1 3 2 等
* 我们就可以添加volatile保证指令重排问题
*/
}
}
}
return lazyHungry;
}
//多线程并发有问题
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyHungry.getInstance();
},String.valueOf(i)).start();
}
}
}