上次写了工厂设计模式,那么今天就来聊聊单例设计模式。
那么什么是单例设计模式呢,简单来说就是确保一个类只能创建一个实例,并且能提供这个类的访问。
实现:使用一个静态私有构造函数,一个静态变量以及一个静态公有函数来实现。
下面举个栗子吧:
假设一个帝王,她同时拥有三个妃子,然后呢这个帝王能干嘛呢?每晚到不同的妃子住处让妃子侍寝是吧,那么既然帝王只有一个,而不同的妃子住在不同的地方,而这个帝王很显然不是什么荒淫无度的君王,那么也就是说每晚只有一个妃子能够获得这个帝王是吧。
好,现在让我们来看看具体怎么实现:
创建帝王类,
public class King {
private static King king = null;
private King() {
}
public static King getKingToSleep() {
if(king == null) {
king = new King();
}
return king;
}
}
这就是单例设计模式的创建方式之一,在调用时再创建对象,所以也被称为懒汉式,但这种方式有个显而易见的缺陷那就是线程不安全,试想如果有多个线程同时访问getKingToSleep()方法,如果前一个线程king为空判定为真,但还未得到new King()的对象而下一个线程也进入这个方法此时king为空依然为true,此时就会创建两个对象,不符合我们单例设计模式的原则,因此这种方式是线程不安全的,那么怎么解决呢?很简单只要加上线程锁就行了。
public class King{
private static King king = null;
private King() {
}
public static synchronized King getKing() {
if(king == null) {
king = new King();
}
return king;
}
}
上面就是加了线程锁后的代码,其实就是synchronized,表示这个类一次只允许一个线程进入。当然这样效率就会很低,因为每次调用getKing()方法都需要加锁。
下面再为大家介绍一种线程安全效率也低的写法吧:
public class King{
private static King king = null;
private King() {
}
public static King getKing() {
if(king == null) {
synchronized(King.class) {
if(king == null) {
king = new King();
}
}
}
return king;
}
}
当然这话种写法也有一个缺点,那就是可能出现king = new King();这个地方并未被初始化但分配了内存地址,也就是说这样第二个线程访问时就会默认取到了实例,但这个实例可能是不完整的。
最后再给大家介绍一种简单好用的创建单例的方式,饿汉式:
public class King {
private static King king = new King();
private King() {
}
public static King getKingToSleep() {
return king;
}
}
而这种方式的弊端也很显而易见,那就是每次加载类都会创建实例,会产生大量垃圾对象。
最后再来介绍一种比较稳妥的方式,那就是内部静态类的方式
public class King {
private static class KingHolder {
private static final King king = new King();
}
private King (){}
public static final King getKingToSleep() {
return KingHolder.king;
}
}