北漂程序员知识点积累分享
当前位置: java > Java_单例模式(Singleton)

Java_单例模式(Singleton)

2019-12-16 分类:java 作者:北漂程序员 阅读(2408)

在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程

单例模式的特点
1. 单例模式只能有一个实例。
2. 单例类必须创建自己的唯一实例。
3. 单例类必须向其他对象提供这一实例

单例模式VS静态类
1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。
2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。
3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。
4. 基于2, 3条,由于单例对象往往存在于DAO层(数据访问层),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。
5. 静态方法有更高的访问效率。
6. 单例模式很容易被测试

静态类和单例模式情景的选择:
情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。
情景二:需要维持一些特定的状态,此时更适合使用单例模式

实现要点
声明为private来隐藏构造器
private static Singleton实例
声明为public来暴露实例获取方法

单例模式主要追求三个方面性能
线程安全
调用效率高
延迟加载

主要有五种实现方式,懒汉式(延迟加载,使用时初始化),饿汉式(声明时初始化),双重检查,静态内部类,枚举

懒汉式,线程不安全的实现
由于没有同步,多个线程可能同时检测到实例没有初始化而分别初始化,从而破坏单例约束

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
    private static Singleton instance;
    private Singleton() {
    };
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式,线程安全但效率低下的实现

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
    private static Singleton instance;
    private Singleton() {
    };
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
由于对象只需要在初次初始化时需要同步,多数情况下不需要互斥的获得对象,加锁会造成巨大无意义的资源消耗

饿汉式
instance在类加载时进行初始化,避免了同步问题。饿汉式的优势在于实现简单,劣势在于不是懒加载模式(lazy initialization)在需要实例之前就完成了初始化,在单例较多的情况下,会造成内存占用,加载速度慢问题 由于在调用getInstance()之前就完成了初始化,如果需要给getInstance()函数传入参数,将会无法实现

1
2
3
4
5
6
7
8
public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {
    };
    public static Singleton getInstance() {
        return instance;
    }
}

双重检查锁定(double checked locking pattern)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Singleton {
    //private static Singleton instance=null;
  private volatile static Singleton singleton;
    private Singleton(){
       
    }
    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}
解析
第一个注意点:使用私有的构造函数,确保正常情况下该类不能被外部初始化(非正常情况比如通过反射初始化,一般使用反射之后单例模式也就失去效果了)。
第二个注意点:getInstance方法中第一个判空条件,逻辑上是可以去除的,去除之后并不影响单例的正确性,但是去除之后效率低。因为去掉之后,不管instance是否已经初始化,都会进行synchronized操作,而synchronized是一个重操作消耗性能。加上之后,如果已经初始化直接返回结果,不会进行synchronized操作。
第三个注意点:加上synchronized是为了防止多个线程同时调用getInstance方法时,各初始化instance一遍的并发问题。
第四个注意点:getInstance方法中的第二个判空条件是不可以去除,如果去除了,并且刚好有两个线程a和b都通过了第一个判空条件。此时假设a先获得锁,进入synchronized的代码块,初始化instance,a释放锁。接着b获得锁,进入synchronized的代码块,也直接初始化instance,instance被初始化多遍不符合单例模式的要求~。加上第二个判空条件之后,b获得锁进入synchronized的代码块,此时instance不为空,不执行初始化操作。
第五个注意点:instance的声明有一个voliate关键字,如果不用该关键字,有可能会出现异常。因为instance = new Test();并不是一个原子操作,会被编译成三条指令:1.给Test的实例分配内存 2.初始化Test的构造器 3.将instance对象指向分配的内存空间(注意 此时instance就不为空)

然后,java会指令重排序,JVM根据处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,使程序在保证业务运行的同时,充分利用CPU的执行特点,最大的发挥机器的性能!简单来说就是jvm执行上面三条指令的时候,不一定是1-2-3这样执行,有可能是1-3-2这样执行。如果jvm是按照1-3-2来执行的话,当1-3执行完2还没执行的时候,如果另外一个线程调用getInstance(),因为3执行了此时instance不为空,直接返回instance。问题是2还没执行,此时instance相当于什么都没有,肯定是有问题的。然后咧,voliate有一个特性就是禁止指令重排序,上面的三条指令是按照1-2-3执行的,这样就没有问题了。

静态内部类
由于内部类不会在类的外部被使用,所以只有在调用getInstance()方法时才会被加载。同时依赖JVM的ClassLoader类加载机制保证了不会出现同步问题

1
2
3
4
5
6
7
8
9
10
public class Singleton {
    private Singleton() {
    };
    public static Singleton getInstance() {
        return Holder.instance;
    }
    private static class Holder{
        private static Singleton instance = new Singleton();
    }
}

枚举单例

1
2
3
4
5
6
enum SingletonDemo{
INSTANCE;
public void otherMethods(){
System.out.println("Something");
}
}

「三年博客,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏

谢谢支持

支付宝
微信
0

谢谢支持

支付宝
微信
标签:

上一篇:

下一篇:

你可能感兴趣

共有 0 条评论 - Java_单例模式(Singleton)

博客简介

北漂程序员的生活积累,了解学习更多内容,微信搜索公众号:技术开发精选学习笔记

最近更新

精彩评论

服务热线:
 

 QQ在线交流