单例模式—层层剖析寻找最高效安全的单例

问题来源

  什么是单例?它的运用场景是什么?

  单例模式是指保证在系统中只存在某类唯一对象。运用场景随处可见,例如工具类、Spring容器默认new对象等。

  单例模式有几种实现方式?

  饿汉式、懒汉式、双重检查锁式、内部类式。

  推荐使用方式?

  饿汉式、内部类式。

饿汉式

  饿汉式顾名思义饿,那么当应用程序一开始类加载,类的对象立马实例化加载至JVM。

 1 public class SingletonClass {
 2     /**
 3      * 优点:调用效率高。
 4      * 缺点:没有延迟加载。
 5      */
 6     private static SingletonClass instance =new SingletonClass();
 7
 8     public static SingletonClass getInstance(){
 9         return instance;
10     }
11 }

  为什么调用效率高?没有延迟加载?

  答:假设在高并发的场景下,有10W+并发调用,不需要同步处理。可以直接在堆内存直接获取对象不需要任何等待。

    同样,它没有延迟加载,如果它是需要消耗很大内存的对象,最开始就加载入堆内存,而用户暂时不需要。这样就会严重占用堆内存,影响运行效率。

懒汉式

  导引:脑洞大开的程序员们说:上述问题还不简单,当调用的时候在new对象不就行。于是出现了懒汉式的雏形版本。

public class SingletonClass {
    private static SingletonClass instance;

    public static SingletonClass getInstance(){
        if(null==instance){
            instance=new SingletonClass();
        }
        return instance;
    }
}

  懒汉式顾名思义懒,就是延迟加载,当被调用的时候再实例化。

  问题:如果你是初出茅庐的应届生写成这样,估计面试官也不会追究什么。如果你是有一年工作年限的程序员,估计面试官就会声讨你了。假设,并发数10W+,它就将被蹂躏的不堪入目。那么我们需要怎么解决呢?加上同步操作就大功告成。

 1 public class SingletonClass {
 2
 3     //调用效率低、延迟加载
 4     private static SingletonClass instance;
 5
 6     public static synchronized SingletonClass getInstance(){
 7         if(null==instance){
 8             instance=new SingletonClass();
 9         }
10         return instance;
11     }
12 }

  问题:从效率维度考虑,估计这样已经完美了吧?但是,从安全纬度考虑,依然隐隐约约存在问题。如果是接触过反射、反序列化的同学,我们一起来继续探讨。

/**
 * 通过反射破坏懒汉式单例
 * @author aaron
 */
public class Client {
    public static void main(String[] args) throws Exception {
        SingletonClass clazzOne=SingletonClass.getInstance();
        SingletonClass clazzTwo=SingletonClass.getInstance();

        System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());
        System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());

        Class<SingletonClass> clazz=(Class<SingletonClass>)Class.forName("singleton.SingletonClass");
        Constructor<SingletonClass> c=clazz.getConstructor(null);
        c.setAccessible(true);
        SingletonClass clazzThree=c.newInstance();
        SingletonClass clazzFour=c.newInstance();
        System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());
        System.out.println("clazzFour-hasCode:"+clazzFour.hashCode());
    }
}

 1 public class SingletonClass implements Serializable{
 2
 3     private static SingletonClass instance;
 4
 5     public static synchronized SingletonClass getInstance(){
 6         if(null==instance){
 7             instance=new SingletonClass();
 8         }
 9         return instance;
10     }
11
12     public static void main(String[] args) throws Exception {
13         SingletonClass clazzOne=SingletonClass.getInstance();
14         SingletonClass clazzTwo=SingletonClass.getInstance();
15         System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());
16         System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());
17
18
19         FileOutputStream fos=new FileOutputStream(new File("f:/test.txt"));
20         ObjectOutputStream bos=new ObjectOutputStream(fos);
21         bos.writeObject(clazzOne);
22         bos.close();
23         fos.close();
24
25         FileInputStream fis=new FileInputStream(new File("f:/test.txt"));
26         ObjectInputStream bis=new ObjectInputStream(fis);
27         SingletonClass clazzThree=(SingletonClass) bis.readObject();
28         System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());
29     }
30 }

  问题:这么轻易就被破解了?那怎么解决呢?

public class SingletonClass implements Serializable{

    private static SingletonClass instance;

    private SingletonClass(){
        //防止被反射
        if(null!=instance){
            throw new RuntimeException();
        }
    }

    public static synchronized SingletonClass getInstance(){
        if(null==instance){
            instance=new SingletonClass();
        }
        return instance;
    }

    //当没有定义这方法时,反序列化默认是重新new对象。
    //反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
    private Object readResolve() throws ObjectStreamException{
        return instance;
    }
}

双重检查锁与内部类

  双重检查锁与内部类的方式:缘由懒汉式、饿汉式要么存在调用效率低或者运行效率低问题。而这两种方式取前两者的优点为自己所用。

 1 /**
 2  * 单例模式-双重检查锁
 3  * @author aaron
 4  */
 5 public class SingletonClass{
 6     private static SingletonClass instance;
 7
 8     public static  SingletonClass getInstance(){
 9         if(null==instance){
10             synchronized (SingletonClass.class) {
11                 if(instance==null){
12                     instance=new SingletonClass();
13                 }
14             }
15         }
16         return instance;
17     }
18 }

  问题:缘由JVM对于此种方式的同步控制,并不稳定,当高并发的时候,可能会出现问题,并不推荐使用这种方式。理论上来说,它是不存在问题的。

 1 /**
 2  * 单例模式-内部类的方式
 3  * @author aaron
 4  */
 5 public class SingletonClass{
 6
 7     private static class InnerClass{
 8         public static SingletonClass instance=new SingletonClass();
 9     }
10
11     public static SingletonClass getInstance(){
12         return InnerClass.instance;
13     }
14 }

版权声明

  作者:xiaoyongAaron(邱勇)

  出处:http://www.cnblogs.com/qiuyong/

  您的支持是对博主深入思考总结的最大鼓励。

  本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,尊重作者的劳动成果。

时间: 2024-08-03 01:50:03

单例模式—层层剖析寻找最高效安全的单例的相关文章

深入理解单例模式:静态内部类单例原理

本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理.OK,废话不多说,进入正文. 首先我们要先了解下单例的四大原则: 1.构造私有. 2.以静态方法或者枚举返回实例. 3.确保实例只有一个,尤其是多线程环境. 4.确保反序列换时不会重新构建对象. 我们常用的单例模式有: 饿汉模式.懒汉模式.双重锁懒汉模式.静态内部类模式.枚举模式,我们来逐一分析下这些模式的区别. 1.饿汉模式: public class SingleTon{ private static Singl

单例模式序列化后反序列化单例失效的问题

不做处理的情况下,单例模式失效,代码如下: public class User implements Serializable { public String getName() { return name; } public void setName(String name) { this.name = name; } public static final User INSTANCE= new User(); private String name ; // private Object r

Java设计模式之单例模式之登记式单例

package 创建型_单例模式_登记式; import java.util.HashMap; import java.util.Map; /** * 登记式单例实际上维护的是一组单例类的实例,将这些实例存储到一个Map(登记簿) * 中,对于已经登记过的单例,则从工厂直接返回,对于没有登记的,则先登记,而后 * 返回 * @author pp * */ public class RegSingleton { /** * 登记簿,用来存放所有登记的实例 */ private static Map

登记式单例实现单例模式的继承(限定一个抽象类的所有子类都必须是单例)

一直都在想如何在Java写一个抽象类,让该抽象类的所有子类都限定为单例模式,一个类需要设计成单例时直接继承该抽象类,而单例的限定与实例获取交给抽象类来完成.一个传统的单例类形如一下形式: 1 public class Singleton { 2 private static final Singleton singleton = new Singleton(); 3 4 //限制产生多个对象 5 private Singleton(){ 6 } 7 8 //通过该方法获得实例对象 9 publi

【7.13】单例模式(Singleton)的用法和用处以及破解单例

1):用处   是一种创建者模式,只生成一个实例对象,具有全局唯一性,当一个对象的产生需要比较多的资源时, 如读取配置(如数据库连接池.Spring中, 一个Component就只有一个实例Java-Web中, 一个Servlet类只有一个实例等), 产生其他依赖对象, 则可以通过在应用启动时直接产生一个单例对象, 然后永久驻留内存的方式来解决 2):写法 private化构造函数 private Singleton实例 提供一个public的获取实例的方法 主要有五种实现方式,懒汉式(延迟加载

单例模式 俗称单例3步曲+1曲

什么是单例模式?    在整个应用中通过这个类只能实例化一个对象实例的设计模式 模式分类? 在所有模式设计中,有三种基础设计模式,单例模式,工厂模式,注册树模式,其他模式往往基于这几种模式,今天带来的是单例模式. 为什么要用单例模式?    1.php常常和数据库打交道,如果在应用中频繁建立连接对象,进行new操作的话,会消耗大量的系统内存资源.(节省资源开销)    2.在团队合作项目中,单例模式可以有效避免不同程序员new自己的对象时,造成人为的系统消耗.(节省资源开销)  --------

单例模式:dispatch_once创建单例

无论是爱还是恨,你都需要单例. 什么是单例呢?Wikipedia是如此定义的: 在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式. 或者我的理解是: 单例就是类,但是该类只能实例化出一个对象. 尽管这是单例的实际定义,但在Foundation框架中不一定是这样.比如NSFileManger和NSNotificationCenter,分别通过它们的类方法defaultManager和defaultCenter获取.尽管不是严格意义的单例,这些类方法返回一个可

C++单例模式与单例类模板

1.单例模式 (1)需求的提出:在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在(Single,Instance) (2)问题:如何定义一个类,使得这个类最多只能创建一个对象? 要控制类的对象数目,必须对外隐藏构造函数 思路: @1:将构造函数的访问属性设置为private,将拷贝构造函数,赋值操作符都声明成private的,防止对象的赋值操作,因为这个类的对象只能有一个. @2:定义instance并初始化为NULL.在类中用static type* instance的方式声明一

php单例模式实现对象只被创建一次 mysql单例操作类

这是我在php面试题中遇到的一道试题,单例模式按字面来看就是某一个类只有一个实例,这样做的好处还是很大的,比如说数据库的连接,我们只需要实例化一次,不需要每次都去new了,这样极大的降低了资源的耗费. 单例类至少拥有以下三种公共元素: 必须拥有一个构造函数,并且必须被标记为private. 拥有一个保存类的实例的静态成员变量. 拥有一个访问这个实例的公共的静态方法 具体使用方面,在下面php实例中注释的很清楚: 1 <?php 2 /** 3 * by www.phpddt.com 4 */ 5