关于java:单例的7种写法

44次阅读

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

人生有涯,学海无涯

一、概述

单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种类型的设计模式属于创立型模式,它提供了一种创建对象的最佳形式。

这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。

留神:

  • 1、单例类只能有一个实例。
  • 2、单例类必须本人创立本人的惟一实例。
  • 3、单例类必须给所有其余对象提供这一实例。

二、七种实现

2.1、懒汉式 — 线程不平安

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容: 这种形式是最根本的实现形式,这种实现最大的问题就是不反对多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种形式 lazy loading 很显著,不要求线程平安,在多线程不能失常工作。

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {if (instance == null) {instance = new Singleton();  
    }  
        return instance;  
    }  
}

2.2、懒汉式 — 线程平安

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容: 这种形式具备很好的 lazy loading,可能在多线程中很好的工作,然而,效率很低,99% 状况下不须要同步。
长处:第一次调用才初始化,防止内存节约。
毛病:必须加锁 synchronized 能力保障单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很要害(该办法应用不太频繁)。

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();  
    }  
        return instance;  
    }  
}

2.3、饿汉式

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容: 这种形式比拟罕用,但容易产生垃圾对象。
长处:没有加锁,执行效率会进步。
毛病:类加载时就初始化,节约内存。
它基于 classloader 机制防止了多线程的同步问题,不过,instance 在类装载时就实例化,尽管导致类装载的起因有很多种,在单例模式中大多数都是调用 getInstance 办法,然而也不能确定有其余的形式(或者其余的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的成果。

public class Singleton {private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {return instance;}  
}

2.4、饿汉式 — 变种

其实和之前的一样,仅仅是将单例的创立挪到了动态块。

public class Singleton {  
    private static Singleton instance;
    
    static {instance = new Singleton();
    }
    
    private Singleton (){}  
    public static Singleton getInstance() {return instance;}  
}

2.5、枚举式

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容: 这种实现形式还没有被宽泛采纳,但这是实现单例模式的最佳办法。它更简洁,主动反对序列化机制,相对避免屡次实例化。
这种形式是 Effective Java 作者 Josh Bloch 提倡的形式,它不仅能防止多线程同步问题,而且还主动反对序列化机制,避免反序列化从新创立新的对象,相对避免屡次实例化。不过,因为 JDK1.5 之后才退出 enum 个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少用。
不能通过 reflection attack 来调用公有构造方法。

public enum Singleton {INSTANCE;}

2.6、双重校验锁式

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程平安:

实现难度: 较简单

形容: 这种形式采纳双锁机制,平安且在多线程状况下能放弃高性能。
getInstance() 的性能对应用程序很要害。

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

留神:

  1. volatile 的应用,为了避免裸露一个未初始化的不残缺单例实例;
  2. 双重判空校验,第一个判断防止了频繁的加锁,第二个判断能够拦住多余的创立实例的线程;
  3. 加锁,保障了线程平安(只有一个实例)

这种实现形式是经常出现在面试题中的,而且常常会要求手写。

2.7、动态外部类

是否 Lazy 初始化:

是否多线程平安:

实现难度: 个别

形容: 这种形式能达到双检锁形式一样的效用,但实现更简略。对动态域应用提早初始化,应应用这种形式而不是双检锁形式。这种形式只实用于动态域的状况,双检锁形式可在实例域须要提早初始化时应用。

public class Singleton {  
    private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}  
}

三、总结

下面列举的 7 种设计模式中第 1 种线程不平安,能够排除在外,第 3、4 种其实是一种,这样下来其实能够简化为 5 种形式:懒汉、饿汉、动态外部类、枚举、双重校验锁。

正文完
 0