Java设计模式之单例模式,这是最全最详细的了

45次阅读

共计 3579 个字符,预计需要花费 9 分钟才能阅读完成。

一、单例模式
作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问入口
1、单例模式的常用 1.Windows 的任务管理器 2.Windows 的回收站,也是一个单例应用 3. 项目中的读取配置文件的对象 4. 数据库的连接池 5.Servlet 中的 Application Servlet6.Spring 中的 Bean 默认也是单例的 7.SpringMVC Struts 中的控制器
2、单例模式的优点 1. 由于单例模式只生成一个实例,减少了系统给的性能开销,当一个对象需要产生时,当时消耗的资源较多。那么产生对象时构建的方式就可以通过单例去构建。2. 单例模式存在全局访问点,所以可以优化共享资源访问。
3、常见的单例模式的构建方法 1. 饿汉式:线程安全 调用率高 但是不能延迟加载 2. 懒汉式:线程安全 调用率不高 但是可以延迟加载 3. 双重检测 (double check)4. 静态内部类 (线程安全 可以延迟加载)5. 枚举单例 线程安全 不可以延迟加载
二、代码案例展示
1、饿汉式
/**
* 饿汉式:
* 类只要被加载就会被加载全局变量,所以饿汉式,会被及时加载。(没有懒加载)
* 并且存在天然的线程安全问题。
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public class SingleHungry {
// 提供静态的全局变量 作为访问该类实例的入口
private static SingleHungry sh = new SingleHungry();
/**
* 构造器私有 无法创建对象
*/
private SingleHungry(){

}
/**
* 对外提供 get 方法获取 该类的实例
* @return
*/
public static SingleHungry getInstance(){
return sh;
}
}

2、懒汉式
/**
* 懒汉式:
* 全局变量初始化放到了实例化方法中,延迟产生对象。
* 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public class SingleLazy implements Serializable{
// 提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载
private static SingleLazy sh = null;

/**
* 构造器私有 无法创建对象
*/
private SingleLazy(){
System.out.println(“ 构造函数被调用了 ”);
}

/**
* 对外提供 get 方法获取 该类的实例
* @return
* @throws InterruptedException
*/
public static synchronized SingleLazy getInstance() {
if(sh==null){
sh = new SingleLazy();
}
return sh;

}

}

上海尚学堂 java 培训 shsxt.com
3、双重检测
/**
* 懒汉式:
* 全局变量初始化放到了实例化方法中,延迟产生对象。
* 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public class SingleLazy4 {
// 提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载
private volatile static SingleLazy4 sh = null;

/**
* 构造器私有 无法创建对象
*/
private SingleLazy4(){
System.out.println(“ 被调用了 ”);
}

/**
* 双重校验锁式(也有人把双重校验锁式和懒汉式归为一类)分别在代码锁前后进行判空校验
*,双重校验锁式是线程安全的。然而,在 JDK1.5 以前,DCL 是不稳定的,有时也可能创建多个实例,
* 在 1.5 以后开始提供 volatile 关键字修饰变量来达到稳定效果。
* 双重校验锁 DCL(double checked locking)
* @return
* @throws InterruptedException
*/
public static SingleLazy4 getInstance() {
if(sh == null){
synchronized(SingleLazy4.class){
if(sh == null){
sh = new SingleLazy4();
//return singleton; // 有人提议在此处进行一次返回
}
//return singleton; // 也有人提议在此处进行一次返回
}
}
return sh;
}
}

上海尚学堂 Java 培训 shsxt.com 获取更多 java 学习资料
4、静态内部类
/**
* 静态内部类
*
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public class SingleInner {

/**
* 静态内部类式和饿汉式一样,同样利用了 ClassLoader 的机制保证了线程安全;
* 不同的是,饿汉式在 Singleton 类被加载时(从代码段 3 - 2 的 Class.forName 可见)
* 就创建了一个实例对象,而静态内部类即使 Singleton 类被加载也不会创建单例对象,
* 除非调用里面的 getInstance() 方法。因为当 Singleton 类被加载时
*,其静态内部类 SingletonHolder 没有被主动使用。只有当调用 getInstance 方法时,
* 才会装载 SingletonHolder 类,从而实例化单例对象。

这样,通过静态内部类的方法就实现了 lazy loading,很好地将懒汉式和饿汉式结合起来,
既实现延迟加载,保证系统性能,也能保证线程安全
*/
private static class SingleInnerHolder{
private static SingleInner instance = new SingleInner();
}

private SingleInner(){
System.out.println(“ 我被调用了 ”);
}
public static SingleInner getInstance(){
return SingleInnerHolder.instance;
}
}
5、枚举单例
/**
* jvm 提供底层保证
* 不可能出现序列化、反射产生对象的漏洞 但是不能做到延迟加载
在外部,可以通过 EnumSingleton.INSTANCE.work() 来调用 work 方法。默认的枚举实例的创建是线程安全的
、,但是实例内的各种方法则需要程序员来保证线程安全。
总的来说,使用枚举单例模式,有三个好处:
1. 实例的创建线程安全,确保单例。2. 防止被反射创建多个实例。3. 没有序列化的问题。
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public enum SingleEnum {
// 实例化对象
INSTANCE;

/**
* 对象需要执行的功能
*/
void getInstance(){

}
}

上海尚学堂 java 培训 shsxt.com
6、反射 / 序列化,获取对象,以及防止方式
import java.io.ObjectStreamException;
import java.io.Serializable;

/**
* 懒汉式:
* 全局变量初始化放到了实例化方法中,延迟产生对象。
* 但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。
* @author 码歌老薛
* @date 创建时间 猴年马月
* @version 1.0
*/
public class SingleLazy implements Serializable{
// 提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载
private static SingleLazy sh = null;

/**
* 构造器私有 无法创建对象
*/
private SingleLazy(){
if(sh!=null){
throw new RuntimeException();
}
System.out.println(“ 构造函数被调用了 ”);
}

/**
* 对外提供 get 方法获取 该类的实例
* @return
* @throws InterruptedException
*/
public static synchronized SingleLazy getInstance() {
if(sh==null){
sh = new SingleLazy();
}
return sh;

}

private Object readResolve()throws ObjectStreamException{
return sh;
}

}

上海尚学堂 java 培训 shsxt.com
三、用法总结
1、懒汉式效率是最低的。2、占用资源少 不需要延时加载 枚举优于 饿汉式 3、占用资源比较多 需要延时加载 静态内部类 优于 懒汉式
更多 Java 技术文章欢迎阅读上海尚学堂 Java 培训,免费试学和线上公开课培训课程等你学习。

正文完
 0