国际惯例,扯闲话
第一次写博客,新手一名,轻喷~~
核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
应用场景
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都new一个对象去读取。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 在servlet编程中,每个servlet也是单例
单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
常见的五种单例模式实现方式
- 饿汉式(线程安全,调用效率高,但是,不能延时加载)
- 懒汉式(线程安全,调用效率不高,但是,可以延时加载)
- 双重检测锁式(由于JVM内部模型原因,偶尔会出现问题,不建议使用)
- 静态内部类式(线程安全,调用效率高,但是,也可以延时加载,是不是很完美?差不多就是用它了!)
- 枚举单例(线程安全,调用效率高,但是不能延时加载)
饿汉式实现(单例对象立即加载)
public class SingletonDemo {
//1、提供一个静态属性。类加载时,立即加载该对象,天然的是线程安全的
private static /*final*/ SingletonDemo singletonDemo = new SingletonDemo();
//2、私有化构造器(这样别人就访问不了了,但是反射好像可以...)
private SingletonDemo() {
}
//3、提供一个开放的静态方法,将该实例返回。方法没有同步,调用效率高
public static /*synchronized*/ SingletonDemo getInstance() {
return singletonDemo;
}
}
static变量在类装载时初始化,此时也不会发生多个线程对象访问该对象的问题。 虚拟机保证只会装一次该类,肯定不会发生并发访问的问题。所以synchronized可以省略。
问题:
如果只是加载该类,而不是调用getInstance(),甚至永远没有调用,则会造成资源浪费!
懒汉式实现(单例对象延迟加载)
public class SingletonDemo02 {
//1、声明对象。只做声明,类初始化时不加载这个对象,实现延迟加载,真正用的时候才创建
private static SingletonDemo02 instance;
//2、将构造器私有化
private SingletonDemo02() {
}
//3、提供一个开放方法,返回实例对象。方法同步,调用效率低。
public static synchronized SingletonDemo02 getInstance() {
if (instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
}
要点:
延迟加载,懒加载,即真正用的时候才加载。
问题:
资源利用率变高了,但是,每次调用getInstance()方法都需要同步,并发效率比较低。
双重检测锁实现
public class SingletonDemo03 {
//1、声明实例对象
private static SingletonDemo03 instance = null;
//2、私有化构造器
private SingletonDemo03() {
}
//3、提供开放的获取实例对象方法
public static SingletonDemo03 getInstance() {
if (instance == null) {
SingletonDemo03 s;
synchronized (SingletonDemo03.class) {
s = instance;
if (s == null) {
synchronized (SingletonDemo03.class) {
if (s == null) {
s = new SingletonDemo03();
}
}
instance = s;
}
}
}
return instance;
}
}
将方法同步内容移动到if内,提高了执行的效率,不必每次都同步,只有第一次才同步,对象创建了之后就没必要了
问题:
由于编译器优化,JVM底层内部模型的原因,偶尔会出现问题,不建议使用
静态内部类实现(也是一种懒加载)
public class SingletonDemo04 {
//1、创建私有的静态内部类,在该类中new出实例对象
private static class SingletonInstanceClass{
private static final SingletonDemo04 instance = new SingletonDemo04();
}
//2、构造器私有化
private SingletonDemo04() {
}
//3、提供外部可访问的方法将实例对象返回
public static SingletonDemo04 getInstance() {
return SingletonInstanceClass.instance;
}
}
要点:
- 外部没有static属性,则不会像饿汉式那样立即加载对象
- 只有真正调用getInstance()方法,才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证内存中只有一个实例存在,且只能赋值一次,从而保证了线程安全性。
- 兼并了并发高效调用和延迟加载的优势
枚举实现
public enum SingletonDemo05 {
//定义一个枚举元素,它就代表了Singleton的一个实例
INSTANCE;
//单例可以有自己的操作
public void singletonOperation() {
//功能处理
}
}
优点:
- 实现简单
- 枚举本省就是单例模式。由JVM从根本上提供保障,避免通过反射和序列化的漏洞
缺点:
无延迟加载
结语
有什么补充的欢迎大家留言,后面会把剩下的设计模式也写一下,如果觉得帮助到你了,就请点个赞,哈哈哈!