设计模式之单例模式

写在最前面

单例的文章很多,所以本文主要想记录下几种常用单例的不好理解之处

  • 双重检测 静态资源上的volatile 关键字作用 ?
  • 静态内部类怎么实现线程安全单例 ?
  • 枚举单例怎么理解 ?

单例的概念

  • 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。

  • Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
    详情见百度百科

单例的特点

  • 一是某个类只能有一个实例
  • 二是它必须自行创建这个实例
  • 三是它必须自行向整个系统提供这个实例
    详情见百度百科

单例的几种写法

  • 饿汉模式 (线程安全 低效)
/**
 * 类加载时就创建实例
 */
public class Singleton {
	private Singleton(){}
    private final static Singleton singleton= new Singleton();
    public static Singleton getInstance(){
        return singleton;
    }
}
/**
 * 静态代码块实现(类加载时)
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    static {
        singleton= new Singleton();
    }
    public static Singleton getInstance() {
        return singleton;
    }
}
  • 懒汉模式 (线程不安全)
 public class Singleton {
 	private Singleton(){}
    private static Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  • 懒汉加锁 (线程安全 低效)
/**
 * 方法加锁
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    public static synchronized Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
/**
 * 代码块加锁
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}
  • 双重检测 (线程安全 高效)
创建实例代码 singleton = new Singleton();  主要做了三件事     
		1.开辟空间  
	 	2.创建对象  
	 	3.地址指向
不加volatile:可能执行顺序为132 这时第二个获取资源会是地址不为空但是对象实例为空
加上volatile:防止JVM指令重排 确保执行顺序为123 确保要么静态变量为NULL 要么有地址指向并且对象实例也不为空
public class Singleton {
	private Singleton() {}
    private static volatile Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                if (null == singleton) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
  • 静态内部类 (线程安全 高效)
静态内部类是怎么保证线程安全的呢? 

虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,
那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。

如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞 ( 需要注意的是,其他线程虽然会被阻塞,
但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。)

在实际应用中,这种阻塞往往是很隐蔽的
public class Singleton {
    private Singleton() {}
    private static class SingletonInner {
        private static final Singleton singleton= new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInner.singleton;
    }
}
  • 枚举 (线程安全 高效)
枚举实现单例可能不太好理解
其实在编译成class文件时,会默认为枚举类成员加上 final static修饰
再想想就和静态内部类的实现几乎一样了
在实际代码中可以将需要执行一次的可写在私有方法中
public enum Singleton {
    INSTANCE;
    public void methodA() {
    }
    // 枚举中私有方法只会执行一次
    private void methodB() {
    }
}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值