单例的五种方式
单例模式是Java开发中常用的模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前也在学习设计模式的过程中对单例模式有所了解。有两种创建单例的思想,饿汉模式和懒汉模式。除此之外还有几种优秀高效的单例方法。
“在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。”
1、饿汉模式
饿汉模式就是立即加载,一般情况下再调用getInstance()方法之前,类加载的时候已经产生了实例。
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
缺点:占用资源,当单例类很大的时候,其实我们是想使用的时候再产生实例,如果用不到就会白白占用资源。因此这种方式适合占用资源少,在初始化的时候就会被用到的类。
2、懒汉模式
懒汉模式就是延迟加载,也叫懒加载。在程序需要用到的时候再创建实例,这样保证了内存不会被浪费。
2.1、懒汉模式
懒汉模式常规写法如下:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null != singleton) {
singleton = new Singleton();
}
return singleton;
}
}
缺点:实现方式是线程不安全的,多线程并发的环境下可能出现资源同步问题。导致创建多个单实例对象:
2.2、synchronized懒汉模式
多线程下的资源同步问题一般需要同步,最简单粗暴的方式加个synchronized关键字同步方法。
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (null == singleton) {
singleton = new Singleton();
}
return singleton;
}
}
缺点:效率低,同步运行,下个线程想要取得对象,就必须要等上一个线程释放,才可以继续执行。
即使不同步整个方法,而是仅对其中的部分代码块加锁同步,还是需要同步,效率不高。
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
return singleton;
}
3、DCL双重检查锁机制 √
DCL(Double Checked Locking,双重检查锁) 双重检查锁机制很好的解决懒加载单例模式的效率问题和线程安全问题。这也是在项目中最常用到的方式。第一次看到师兄的代码单例模式就是这样写的,当时还看不懂,没想到大有文章。
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null == singleton)
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
注意到在定义singleton的时候用到了volatile关键字,这是为了防止指令重排序的,或多或少对性能也有些影响。这样的写法非常讲究,更多参考一篇博客
4、静态内部类(单例最优解)
靠JVM保证类的静态成员只能被加载一次的特点,这样就从JVM层面保证了只会有一个实例对象。
public class SingletonInner {
private SingletonInner() {
}
private static class Singleton{
private static SingletonInner singletonInner = new SingletonInner();
}
public static SingletonInner getInstance(){
return Singleton.singletonInner;
}
}
加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
5、静态代码块 √
和饿汉模式类似
//使用静态代码块实现单例模式
class SingletonStaticBlock {
private static SingletonStaticBlock singletonStaticBlock;
static {
singletonStaticBlock = new SingletonStaticBlock();
}
public static SingletonStaticBlock getInstance() {
return singletonStaticBlock;
}
}
参考博客:
Java实现单例的五种方式