单例模式详解

pushpin 单例模式

单例模式: 一个类有且只有一个实例,且提供一个全局访问方法来访问这个实例。

核心点: 控制类实例数量

范围: 同一个JVM环境下

实现思路:

  1. 该实例类提供私有的构造函数,防止外部类创建该类实例
  2. 定义该类对象为私有且静态类型
  3. 提供一个全局可以访问该类实例的静态方法

pushpin 饿汉式

/**
 * @Description 饿汉式单例
 * @Author simonsfan
 */
public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getSingleton() {
        return singleton;
    }
}

缺点是在类加载的时候就实例化了对象,有点浪费空间,优点是线程安全,因为static变量会在类装载的时候初始化,并且多个实例的static变量会共享一块内存区域。

pushpin 懒汉式,先有第一版:

/**
 * @Description 懒汉式单例
 * @Author simonsfan
 */
public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

pushpin 但是由于线程不安全,于是加上了synchronized关键字修饰方法

/**
 * @Description 懒汉式单例
 * @Author simonsfan
 */
public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

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

pushpin 双锁检测(DCL)版本的单例模式

/**
 * @Description DCL单例模式
 * @Author simonsfan
 */
public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {
    }

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

DCL版本的单例模式中,加入了volatile关键字,这个关键字有很重要的特性之一就是:volatile修饰的变量保证对所有线程可见性,它是java虚拟机提供的最轻量级别的同步机制。同时在getSingleton方法中,先判断了实例为空才加锁,所以DCL版本的单例模式既能相对提高性能,又能解决线程安全问题,但其中还是使用到了synchronized锁,无疑还是会产生点影响,于是我们又演化出了静态内部类的单例版本。

首先,我们知道类加载的时机有如下五种情况:

取自《深入理解Java虚拟机 周志明 第二版》第七章节

 pushpin 静态内部类实现的单例模式

/**
 * @Description 静态内部类单例模式
 * @Author simonsfan
 */
public class Singleton {

    private Singleton() {
    }

    static class SingletonHolder {
        private static Singleton singleton = new Singleton();
    }

    public static Singleton getSingleton() {
        return SingletonHolder.singleton;
    }
}

从上图我们可以知道,Singleton类在被jvm加载时,内部类SingletonHolder并不会被加载,只有调用静态getSingleton方法时候才会加载,达到了懒加载的目的;同时由于Singleton类实例是在内部类SingletonHolder中完成的,静态内部类只会加载一次,jvm的类加载机制能保证这个过程中的线程安全,所以静态内部类实现方式做到了懒加载、线程安全而不加锁,比DCL实现方式更加优雅。


--------------------------------------------------2018-06-30补充--------------------------------------------

pushpin 枚举实现单例

《Effective Java》里描述道: "单元素的枚举类型是java1.5版本以来实现单例的最佳方法"

/**
 * @Description 枚举版本单例模式
 * @Author simonsfan
 */
public class Singleton {

}

/**
 * @Description 枚举实现单例
 * @Author simonsfan
 */
public enum SingletonEnum {

    private Singleton singleton;

    SingletonEnum(){
        singleton = new Singleton();
    }

    public Singleton getSingleton() {
        return singleton;
    }
}

/**
 * @Description 模拟调用者
 * @Author simonsfan
 */
public class SingletonClient {
    public static void main(String[] args) {
        Singleton singleton1 = SingletonEnum.singletonInsance.getSingleton();
        Singleton singleton2 = SingletonEnum.singletonInsance.getSingleton();
        System.out.println(singleton1==singleton2);
    }
}

books 引申阅读: 代理模式详解

books 引申阅读: 策略模式详解

books 参考资料:《深入理解Java虚拟机》周志明 第二版第七章节 、《Effective Java》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值