一、为什么要使用单例设计模式(即单例设计模式的好处)。
对于java语言而言,使用单例设计模式有两大好处。
1、对于频繁使用的对象,可以节省创建对象所带来的开销,对于那些重量级对象而言,这可是节省了一笔非常可观的系统开销。
2、对于整个系统而言,由于new操作次数的减少,因此系统内存使用的频率也会降低,这将减少GC的压力,缩短GC停顿的时间。
二、对于单例设计模式最简单的实现步骤
第一步:构造方法的私有,保证从外部无法通过new的方式创建对象。
第二步:对外提供获取该类实例的静态方法。
第三步:在该类的内部创建该类的实例对象,通过第二步的静态方法返回。
三、详解单例设计模式的几种实现方式以及其优缺点。
1、饿汉式单例设计模式(当类加载时,就实例化单例对象)
package designer.model.demo;
/**
* 饿汉式单例设计模式
* @author igorea
*
*/
public class SingletonModel {
//第二步:内部实例化
private static SingletonModel singletonModel = new SingletonModel();
//第一步:构造方法的私有化
private SingletonModel(){
}
//第三步:为外部提供一个获取实例的静态方法
public static SingletonModel getSingletonModel(){
return singletonModel;
}
}
优点:实现简单,而且是线程安全的。
缺点:无法实现延迟加载,假如单例的创建过程十分缓慢,由于实例成员变量又是static的,在加载该类时,就会去实例化该成员变量。在未用到该实例成员时就去实例化该对象可能不是一种很好的做法。
2、懒汉式单例设计模式(实现延迟加载)
package designer.model.demo;
/**
* 懒汉式单例设计模式
* @author Igorea
*
*/
public class SingletonDemo2 {
//第二步:声明成员static变量
private static SingletonDemo2 instance = null;
//第一步:构造方法私有化
private SingletonDemo2(){}
//第三步:对外提供获取对象实例的静态方法
public static SingletonDemo2 getInstance(){
if(instance == null) instance = new SingletonDemo2();
return instance;
}
}
优点:实现了延迟加载
缺点:懒汉式单例设计模式是非线程安全的。(至于为什么,自己去细细体会吧!)
3、饱汉式单例设计模式(由于懒汉式的缺点我们不得不继续改进)
package designer.model.demo;
/**
* 饱汉式单例设计模式
* @author Igorea
*
*/
public class SingletonModel3 {
//第二步:声明其实例
private static SingletonModel3 instance = null;
//第一步:构造方法的私有化
private SingletonModel3(){}
//第三步:对外提供获取实例的方法
public static synchronized SingletonModel3 getInstance(){
//如果这里还想做一些其他的事情,将整个函数枷锁可能不是一个好的选择
if(instance == null){
instance = new SingletonModel3();
}
return instance;
}
}
优点:线程安全
缺点:原本还可以并发做一些事情,但你却锁住了原本可以并发完成的事,造成了系统资源的一种浪费。
4、痴汉式单例设计模式(在饱汉式的基础之上改进,谁叫你吃饱了撑的把不需要锁的通通给我锁住呢?我肯定要改进你)
package designer.model.demo;
/**
* 痴汉式单例设计模式
*
* @author Igorea
*
*/
public class SingletonModel4 {
// 第二步:声明其实例
private static SingletonModel4 instance = null;
// 第一步:私有化构造函数
private SingletonModel4() {
}
// 第三步:对外提供方法获取该实例
public static SingletonModel4 getInstance() {
if (instance == null) {
synchronized (SingletonModel4.class) {
if (instance == null) {
instance = new SingletonModel4();
}
}
}
return instance;
}
// 也是获取实例的方法,写法不一样,暂且称呼它为版本2把
public static SingletonModel4 getInstanceVersion2() {
synchronized (SingletonModel4.class) {
if (instance == null) {
instance = new SingletonModel4();
}
}
return instance;
}
}
优点:线程安全,而且没有像饱汉式一样把不需要锁的代码一起锁住
缺点:同饿汉式单例设计模式相比,性能开销过大。你可能没有什么概念,让我们做个试验吧。
5、壮汉式(这是目前为止较完美的一种实现方式)
/**
* 壮汉式单例设计模式
*
* @author Igorea
*
*/
public class SingletonDemo5 {
// 第一步:私有化构造方法
private SingletonDemo5() {
}
// 第二步:使用内部类实例化
public static class SingletonHolder
{
private static SingletonDemo5 instance = new SingletonDemo5();
}
public static synchronized SingletonDemo5 getInstance(){
return SingletonHolder.instance;
}
}
壮汉式设计模式算是目前为止最好的实现方式了。但我们对单例模式的探讨还没有结束哦。
6、美汉式设计模式(之所以我将他命名为美汉,是因为它真的是目前最完美的单例实现方式)
package designer.model.demo;
/**
* 美汉式(枚举单例模式)
* @author Igorea
*
*/
public enum SingletonDemo6 {
INSTANCE;
//注:就算这里没有指定该类构造方法的访问权限,这里的访问权限也不是default,而是private的
SingletonDemo6() {
}
}
但就这样的话,读者肯定有点懵。(其实我最开始接触到的时,脱口而出的也是 what is this?),那我们就来写一个例子,来帮助大家理解。
例:
package designer.model.demo;
public class Singleton {
}
接下来正式实现部分
package designer.model.demo;
/**
* 美汉式(枚举单例模式)
* @author Igorea
*/
public enum SingletonDemo6 {
INSTANCE;
private Singleton singleton;
//注:就算这里没有指定该类构造方法的访问权限,这里的访问权限也不是default,而是private的
SingletonDemo6() {
singleton = new Singleton();
}
public Singleton getInstance(){
return singleton;
}
public static void main(String[] args) {
Singleton s1 = SingletonDemo6.INSTANCE.getInstance();
Singleton s2 = SingletonDemo6.INSTANCE.getInstance();
System.out.println(s1 == s2);
}
}
最后返回的结果是true。
缺点:也像饿汉式一样,没有实现懒加载。
优点:功能完整、使用简洁、无偿地提供了序列化机制、在面对复杂的序列化或者反射攻击时仍然可以绝对防止被多次实例化。
要向知道为什么?请参考博文https://blog.csdn.net/gavin_dyson/article/details/70832185.