首先总结下但是模式是什么:
单例模式是众多设计模式的一种,那啥是设计模式呢,就像下棋的棋谱一样,棋谱就是前人大佬们总结出来的一些 固定的套路和经验,然后你学习了这个棋谱之后,不能说你下棋的技术有多好,但是一定不会太差,设计模式就是在我们在软件开发中的棋谱,就是不一定你写的代码有多牛,但是按照一定的设计模式来写代码,代码质量不会很差。
单例模式就是字面意思,就是只有一个实例(对象),在语法阶段我们知道,类就相当于是一张图纸,对象就是根据这章图纸锁创建出来的实物。
在Java中,单例模式就是在某个进程中,每个类对应的只有唯一一个实例(也就是只能创建一个对象)。
单例模式中的两种实现模式:1. 饿汉模式 2.懒汉模式
饿汉模式:就是表现的很急迫的意思,在计算机中的意思就是当一个类被加载时创建出了这个唯一的实例(对象),可以类比一下,吃完饭刷碗的例子,饿汉模式就是吃完饭就赶紧去把碗刷了。
懒汉模式:就是很从容,一点也不着急。在计算机中就是非必要的时候,不去创建这个实例,只有当我们用到这个实例的时候才会进行创建,类比刷碗这个例子就是我现在吃完饭了,但是我先不刷碗,当吃下一顿饭的时候,我再去刷碗。
注意:这里的懒汉模式是一个褒义词,在计算机中,有很多表现的是懒的行为,比如我要看一个很大的文件,饿汉模式就是把这个文件全部加载出来再给我呈现到屏幕上,但是懒汉模式只是先将我这个屏幕能够显示多大的文件先展示出来 ,随后如果我要翻页看后边的内容再去计算机再给我去加载后面的内容,比起饿汉模式(可能我看这个文件需要等几分钟),懒汉模式就很香。
如下代码我们可以通过一些语法的限制,实现出一个单例模式的类:
//把这个类设定成单例的
class Singleton {
//唯一实例的本体
private static Singleton instance = new Singleton();
//获取到实例的方法
public static Singleton getInstance() {
return instance;
}
//把构造器私有化 就不能new对象了
private Singleton() { }
}
public class ThreadDemo19 {
public static void main(String[] args) {
//此时s1 和 s2 是同一个对象!!
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//Singleton s3 = new Singleton();
}
}
我们在这个类中如何让这个类只能创建一个对象:构造方法私有化。
首先我们在类中去new出这个类的唯一实例(对象),设置成private权限,在类外获取这个对象就得用get方法,否则在类外是获取不到这个实例的。然后在main方法中获取这个实例的时候可以打印看一下,获取的两个实例时同一个对象,因为引用所指向的对象时相同的,此时我们把构造器私有化,当我们再再类外去new对象的时候,就会编译报错,就完成了单例模式中的饿汉模式。
代码实现懒汉模式:
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
//这个条件判定是否需要加锁,如果对象已经new出来了,就不用加锁了
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() { }
}
public class ThreadDemo20 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}
首先前文已经总结过,懒汉模式就是非必要,不创建对象。所以饿汉模式是在类加载的时候就创建出来了唯一实例,但是懒汉模式是当调用getInstance方法的时候再去创建唯一实例,所以,可看到上述代码,当去调用类方法的时候再去创建对象,在类外获取对象的时候,获取到的依然是同一个对象,就完成了懒汉模式。
关于两个模式是否是线程安全的:
显而易见,饿汉模式就是线程安全的,当类加载的时候,这个唯一实例就已经创建了,再类外也无法new对象了,但是懒汉模式不是线程安全的,问题就出在了if判定上,会出现下图中的bug。
所以在写懒汉模式时,要将懒汉模式变成线程安全的,就要进行加锁操作,让if判定和instance赋值变成一个原子操作,但是加锁是会进行阻塞的,只要线程阻塞,效率就会降低,所以我们可以看下,只有第一次new对象的时候会出现线程不安全,但是只要创建出来了这个对象,以后也就线程安全了,所以我们再加一个判断条件,判断是否需要加锁,当instance是空的时候,如果两个线程并发执行,就不是线程安全的,但是new了唯一对象之后,instance就不空了,就不会进行new操作了,此时就是线程安全的了。所以写出上述代码。
(
加锁,但是加锁就意味着效率的降低,如果在get方法加锁,以后每次调用get方法 都可能进行锁竞争,可以看下只有在首次创建对象的时候是 * 需要加锁的,只要对象创建出来,后面就不用加锁了,所以再判断下是否需要枷锁的 情况
)