分析java中全面的单例模式多种实现方式

一、单例模式的思想

想整理一些 java 并发相关的知识,不知道从哪开始,想起了单例模式中要考虑的线程安全,就从单例模式开始吧。以前写过单例模式,这里再重新汇总补充整理一下,单例模式的多种实现。

单例模式的主要思想是:

  • 将构造方法私有化( 声明为 private ),这样外界不能随意 new 出新的实例对象;
  • 声明一个私有的静态的实例对象,供外界使用;
  • 提供一个公开的方法,让外界获得该类的实例对象

这种说法看上去没错,但也好像不太准确。其实,就算外界能随意 new 出新的实例对象,但只要我们保证我们每次使用的对象是唯一的,就可以。

二、单例模式的 N 种实现方式

2.1、饿汉式(线程安全,可用)

public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton = new Singleton();

    public static Singleton getInstance() {
        return sSingleton;
    }
}
  • 缺点: 类一加载的时候,就实例化,提前占用了系统资源。

2.2、常量式(线程安全,可用)

public class Singleton {
    private Singleton() {
    }

    public static final Singleton sSingleton = new Singleton();
}

将实例对象用 public static final 修饰,不提供公开方法获取实例,直接通过 Singleton.sSingleton 获取。

  • 缺点:与饿汉式一样,类一加载的时候,就实例化,提前占用了系统资源。

2.3、懒汉式(线程不安全,并发场景不可用)

public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton;

    public static Singleton getInstance() {
        if (sSingleton == null) {
            sSingleton = new Singleton();
        }
        return sSingleton;
    }
}
  • 缺点:第一次第一次加载时反应稍慢,线程不安全。

2.4、同步的懒汉式?(线程安全,可用,不建议使用)

public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton;

    public synchronized static Singleton getInstance() {
        if (sSingleton == null) {
            sSingleton = new Singleton();
        }
        return sSingleton;
    }
}
  • 缺点:第一次加载时反应稍慢,每次调用 getInstance 都进行同步,造成不必要的同步开销,这种模式一般不建议使用。

2.5、双重检查锁 DCL (线程安全,大多数场景满足需求,推荐使用)

public class Singleton {
    private Singleton() {
    }

    /**
     * volatile is since JDK5
     */
    private static volatile Singleton sSingleton;

    public static Singleton getInstance() {
        if (sSingleton == null) {
            synchronized (Singleton.class) {
                // 未初始化,则初始instance变量
                if (sSingleton == null) {
                    sSingleton = new Singleton();
                }
            }
        }
        return sSingleton;
    }
}

sSingleton = new Singleton() 不是一个原子操作。(XXX)故须加 volatile 关键字修饰,该关键字在 jdk1.5 之后版本才有。

  • 优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
  • 缺点:第一次加载时反应稍慢,也由于Java内存模型的原因偶尔会失败。在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例对象,并且能够在绝大多数场景下保证单例对象的唯一性,除非你的代码在并发场景比较复杂或者低于jdk1.6版本下使用,否则这种方式一般能够满足需求。

2.6、静态内部类(线程安全,推荐使用)

public class Singleton {

    private Singleton () {
    }

    private static class InnerClassSingleton {
     private final static Singleton sSingleton = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClassSingleton.sSingleton;
    }
}

优点:推荐使用。

2.7、枚举单例(线程安全,不建议使用)

public enum Singleton{
    INSTANCE;

    // 其它方法
    public void doSomething(){
        ...
    }
}
  • 优点:枚举实现单例很简单,也很安全。
  • 缺点:经验丰富的 Android 开发人员都会尽量避免使用枚举。官方文档有说明:相比于静态常量Enum会花费两倍以上的内存。

2.8、另类实现——利用容器实现单例

import java.util.HashMap;
import java.util.Map;

public class Singleton {
    private static Map<String, Object> objMap = new HashMap<String, Object>();

    private Singleton() {
    }

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}

利用了 HashMap 容器 key 不可重复的特性。

  • 优点:这种实现方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一接口进行获取操作,降低用户使用成本,也对用户隐藏了具体实现,降低耦合度。
  • 缺点:没有私有化构造方法,用户可以 new 出新的实例对象。

2.9、防止反射破坏单例

前面的多种实现方法中,很多我们按照构造方法私有化的思想来实现的,我们知道,利用反射,仍然可以创建出新对象,这样在反射场景中,这种思想实现的单例模式就失效了,那么如何防止反射破坏单例模式呢?原理上就是在存在一个实例的情况下,再次调用构造方法时,抛出异常。下面以静态内部类的单例模式为例:

public class Singleton {
    private static boolean flag = false;  

    private Singleton(){
        synchronized(Singleton.class)
        {
            if(flag == false)
            {
                flag = !flag;
            }
            else
            {
                throw new RuntimeException("单例模式被侵犯!");
            }
        }
    }  

    private static class InnerClassSingleton {
     private final static Singleton sSingleton = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClassSingleton.sSingleton;
    }
}

2.10、防止序列化和反序列化破坏单例

通过序列化可以讲一个对象实例写入到磁盘中,通过反序列化再读取回来的时候,即便构造方法是私有的,也依然可以通过特殊的途径,创建出一个新的实例,相当于调用了该类的构造函数。要避免这个问题,我们需要在代码中加入如下方法,让其在反序列化过程中执行 readResolve 方法时返回 sSingleton 对象。

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

三、结语

有没有一种方式实现的单例模式在任何情况下都是一个单例呢?

有。就是上面说的枚举单例。枚举,就能保证在任何情况下都是单例的,并且是线程安全的。

以上就是分析java中全面的单例模式多种实现方式的详细内容,更多关于java单例模式实现方式的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java单例模式和多例模式实例分析

    本文实例讲述了Java单例模式和多例模式.分享给大家供大家参考,具体如下: 一 单例模式 1 代码 class Boss { private static Boss instance;// 静态成员变量,用来保存唯一创建的对象实例 private Boss () { // 利用私有化构造方法,阻止外部创建对象 } public static Boss findBoss() //检查并确保只有一个实例 { if (instance == null) { System.out.println("当前

  • 浅析Java设计模式编程中的单例模式和简单工厂模式

    单例模式 动机 有时候只有一个类的实例是很重要的.比如,一个系统应该只有一个窗口管理实例. 单例模式是最简单设计模式:类负责实例化自己,确保只有一个实例,并且提供一个访问这个实例的入口. 目的 1. 确保只有一个实例被创建. 2. 提供访问这个实例的入口. 使用final确保被创建一次,private的构造函数确保不被实例化.public的getInstance方法确保外部能够访问.下面是饿汉模式: public class Singleton { private static final Si

  • java 单例模式和工厂模式实例详解

    单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例. 私有的构造方法 指向自己实例的私有静态引用 以自己实例为返回值的静态的公有的方法 饿汉式单例 public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } } 懒

  • 为何Java单例模式我只推荐两种

    双重检查模式 public class Singleton { private volatile static Singleton singleton; //1:volatile修饰 private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { //2:减少不要同步,优化性能 synchronized (Singleton.class) { // 3:同步,线程安全 if (sin

  • java 单例模式(饿汉模式与懒汉模式)

    java 单例模式 饿汉式单例 对于饿汉模式,我们可这样理解:该单例类非常饿,迫切需要吃东西,所以它在类加载的时候就立即创建对象. 懒汉式单例类 对于懒汉模式,我们可以这样理解:该单例类非常懒,只有在自身需要的时候才会行动,从来不知道及早做好准备.它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回. 单例设计模式常用于JDBC链接数据库 注意: 1 我们常用的是第一种饿汉式,因为: (1)既然采用了单例设计模式,就是为了使用单例类的对象

  • JAVA多线程并发下的单例模式应用

    单例模式应该是设计模式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例模式的应用. 首先我们先来看一下单例模式的定义: 一个类有且仅有一个实例,并且自行实例化向整个系统提供. 单例模式的要素: 1.私有的静态的实例对象 2.私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例) 3.公有的.静态的.访问该实例对象的方法 单例模式分为懒汉形和饿汉式 懒汉式: 应用刚启动的时候,并不创建实例,当外部调用该类的实例

  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】

    本文实例讲述了Java设计模式之单例模式.分享给大家供大家参考,具体如下: 单例模式就是产生一个对象实例,供外外部访问. 它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例. 懒汉式,就是在使用这个对象时,才去查看这个对象是否创建,如果没创建就马上创建,如果已经创建,就返回这个实例. 饿汉式,在加载这个类的时候就先创建好一个对象实例,等待调用. 两者的优缺点也能猜到,使用懒汉式,在反应速度上肯定要比饿汉式慢. 但是这个对象如果不被调用,那就节省了cpu和内存资

  • Java单例模式实现静态内部类方法示例

    Singleton是众多设计模式中最容易理解的一种,也是众多设计模式中较为重要的一种设计模式.接下来我们看看具体介绍. Singleton模式实现的重点在于将构造函数私有化(private),并通过提供静态公有函数(public synchronized static xxx getInstance)来获取定义在类中的静态私有成员(private static xxx instance),通过一个简单的判断静态实例是否为空来控制这个类只能够new一次,即控制了一个类只能有单个实例,一般的实现如下

  • Java双重检查加锁单例模式的详解

    什么是DCL DCL(Double-checked locking)被设计成支持延迟加载,当一个对象直到真正需要时才实例化: class SomeClass { private Resource resource = null; public Resource getResource() { if (resource == null) resource = new Resource(); return resource; } } 为什么需要推迟初始化?可能创建对象是一个昂贵的操作,有时在已知的运

  • Java单例模式、饥饿模式代码实例

    class MyThreadScopeData {       // 单例     private MyThreadScopeData() {     }       // 提供获取实例方法     public static synchronized MyThreadScopeData getThreadInstance() {         // 从当前线程范围内数据集中获取实例对象         MyThreadScopeData instance = map.get();      

随机推荐