开发模式其实在平时的开发中有经常用到,但是自己平时也没注意,会用但是不理解,这是很大的问题。
遇到面试或者这方面的问题,不能深入的剖析。
参考:https://blog.csdn.net/zjf280441589/article/details/50235937
https://blog.csdn.net/a394268045/article/details/51801258
单例模式的概念
单例就是只有一个实例,就是对于某一个类,整个工程中,只有一个他的实例,并且这个类通过自行实例化,把这个对象向整个工程提供。
为什么要使用单例呢? 或者这样的好处是什么?
1. 在工程中,有一些核心类用来控制资源,单例只有一个Manager,方便资源之间的互相通信同时降低资源的损耗以及冲突。
如数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因此用单例模式数据库的接连池都是一值存在于内存中,降低了开关的损耗。同时通过连接池可以管理其中的数据库连接,可以避免一条数据库连接同时被多个请求调用。
单例模式的性能追求
1.线程安全
2.调用效率
3.延迟加载
单例模式的实现方法:
1.构造私有
2.静态实例
3.公开获取实例的方法
单例模式实现方式
1.懒汉模式:
//懒汉模式简单实现
public class Singleton {
//类加载的时候,对象没有实例化,做到了延迟加载,使用静态修饰,保证引用在类加载时创建,保证唯一
private static Singleton instance;
//构造私有,外部无法通过构造方法创建类的实例
private Singleton(){
}
//方法不是线程安全的
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2. 懒汉模式进化版(实现线程安全)
//懒汉模式进阶版实现
public class Singleton {
//类加载的时候,对象没有实例化,做到了延迟加载,使用静态修饰,保证引用在类加载时创建,保证唯一
private static Singleton instance;
//构造私有,外部无法通过构造方法创建类的实例
private Singleton(){
}
//给整个getInstance方法加锁,线程安全
public synchronized static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
//但是如果不需要创建(跳过if语句),而是只需要调用instance,则此时还加锁,效率较低
return instance;
}
}
3. 懒汉模式究极版(提高效率)
//懒汉模式究极版实现
public class Singleton {
//类加载的时候,对象没有实例化,做到了延迟加载,使用静态修饰,保证引用在类加载时创建,保证唯一
private static Singleton instance;
//构造私有,外部无法通过构造方法创建类的实例
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
//只在创建实例的时候加锁,实现线程安全
synchronized(instance){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
但是其实虽然加锁了还是会出现不安全的情况:
我们以A、B两个线程为例:
a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性
4. 饿汉模式
//饿汉模式实现
public class Singleton {
//类加载的时候,实例同时加载,没有做到了延迟加载
private static Singleton instance = new Singleton();
//构造私有,外部无法通过
private Singleton(){
}
//方法不需要加锁,效率高
public static Singleton getInstance(){
return instance;
}
}
5. 静态内部类模式
//静态内部类模式实现
public class Singleton {
/*
* 加载一个类时,其内部类不会同时被加载。内部类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
* 因此静态内部类实现了延迟加载
*/
private static class innerClass{
private static final Singleton instance = new Singleton();
}
//构造私有,外部无法通过
private Singleton(){
}
public static Singleton getInstance(){
return innerClass.instance;
}
}
6. 枚举实现单例
基于JVM底层实现,Enum天然的就是单例并且线程安全。
实现对比
方式 | 优点 | 缺点 |
---|---|---|
饿汉式 | 线程安全, 调用效率高 | 不能延迟加载 |
懒汉式 | 线程安全, 可以延迟加载 | 调用效率不高 |
双重检测锁 | 线程安全, 调用效率高, 可以延迟加载 | - |
静态内部类 | 线程安全, 调用效率高, 可以延迟加载 | - |
枚举 | 线程安全, 调用效率高 | 不能延迟加载 |