文章内容
一、什么是单例模式
单例模式简单来说,就是要保证一个类在进程内有且只有一个实例对象。这个类对外提供了唯一获取其实例对象的静态方法,且构造方法私有化。
二、单例模式实现方式
- 懒汉式(4种)
- 饿汉式(2种)
- 静态内部类(1种)
- 枚举(1种)
三、懒汉式
1、方式一
public class SingletonDemo3 {
//直接初始化对象
private static SingletonDemo3 instance;
//构造方法私有化
private SingletonDemo3(){}
//
public static SingletonDemo3 getInstance( ){
if(instance==null){
instance = new SingletonDemo3();
}
return instance;
}
}
1)缺点
并发场景时,线程不安全。
2)结论
不推荐使用。
2、方式二
针对实现方式一来进行升级,加入线程同步锁synchronized,保证线程安全。
public class SingletonDemo4 {
//直接初始化对象
private static SingletonDemo4 instance;
//构造方法私有化
private SingletonDemo4(){}
//
public static synchronized SingletonDemo4 getInstance( ){
if(instance==null){
instance = new SingletonDemo4();
}
return instance;
}
}
1)缺点
由于synchronized加在了方法上,并发场景下都要获取锁,性能较差。
2)结论
不推荐使用。
3、方式三(双重检查+同步锁)
针对实现方式二进行升级,将synchronized放在方法代码块中,保证并发性能。
public class SingletonDemo5 {
//直接初始化对象
private static SingletonDemo5 instance;
//构造方法私有化
private SingletonDemo5(){}
//
public static SingletonDemo5 getInstance( ){
if(instance==null){
synchronized (SingletonDemo5.class){
instance = new SingletonDemo5();
}
}
return instance;
}
}
1)缺点
由于java 虚拟机会对class进行指令重排序(JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能),在JVM中初始化对象分为三步:
- 为初始化对象分配内存空间;
- 初始化实例对象;
- 将初始化对象指定分配好的内存空间。
在发生指令重排序后,执行顺序为:1->3->2 。并发场景下,可能线程1创建单例时执行了1->3,线程2发现单例已创建,其实得到的是未初始化完成的对象,导致NPE异常。
2)结论
不推荐使用。
4、方式四(双重检查+同步锁+volatile)
针对实现方式三存在的问题进行升级,在单例对象变量前加入volatile关键字修饰,防止指令重排序。
public class SingletonDemo6 {
//直接初始化对象
private static volatile SingletonDemo6 instance;
//构造方法私有化
private SingletonDemo6(){}
//
public static SingletonDemo6 getInstance( ){
if(instance==null){
synchronized (SingletonDemo6.class){
if(instance == null){
instance = new SingletonDemo6();
}
}
}
return instance;
}
}
1)结论
推荐使用。
四、饿汉式
饿汉式,简单来说,在JVM加载类的时候就已经创建了对象,典型的以空间换时间。
1、方式一
public class SingletonDemo1 {
//直接初始化对象
private static SingletonDemo1 instance = new SingletonDemo1();
//构造方法私有化
private SingletonDemo1(){}
//
public static SingletonDemo1 getInstance( ){
return instance;
}
}
2、方式二
public class SingletonDemo2 {
//定义初始化对象
private static SingletonDemo2 instance;
//构造方法私有化
private SingletonDemo2(){}
// 静态代码块初始化
static {
instance = new SingletonDemo2();
}
public static SingletonDemo2 getInstance( ){
return instance;
}
}
1)优点
效率高,线程安全。
2)缺点
若是单例对象过多,比较浪费内存。
3)结论
不推荐使用
五、静态内部类
public class SingletonDemo7 {
//构造方法私有化
private SingletonDemo7(){
}
//
public static SingletonDemo7 getInstance( ){
return SingletonInner.instance;
}
/**
* 静态内部类,真正调用时才初始化单例对象
*/ private static class SingletonInner{
private static SingletonDemo7 instance = new SingletonDemo7();
}
}
1、优点
延迟加载,在真正调用时进行初始化对象;线程安全;效率高(不需要同步锁)
2、结论
推荐使用。
六、枚举
public enum SingletonDemo8 {
// 定义一个枚举实例,就代表了该类的单例对象
INSTANCE;
}
1、优点
线程安全,效率高
2、结论
推荐使用。