前言
此篇文章只是本人学习 单列模式(Singleton Pattern) 使用的笔记,如有雷同,纯属缘分!
什么是单例?
单例模式(Singleton Pattern):确保某一个类只有一个实例,并提供该实例的全局访问,其构造函数私有化。它是 Java 中最简单的设计模式(Design Pattern)。
设计模式(Design Pattern):前人对特定问题经过无数次的经验总结之后 ,提出的能够解决它的优雅的方案。它不是一种技术与方法,而是一种思想!
本质:
控制实例数量
三大要点:
线程安全
延迟加载
序列化与反序列化安全
单例的几种写法
1.饿汉式
public class Singleton {
//上去就是干,直接实例化
private static Singleton instances = new Singleton();
//私有化构造函数,阻止实例化对象
private Singleton() {}
//直接返回已经实例化了的对象
public static Singleton getInstances() {
return instances;
}
public void doSomething() {
//doSomething....
}
}
饿汉式 这种实现单例方法是最简单粗暴的,它在类开始加载时就初始化了(但浪费内存),而且在多线程中是安全的,是典型的空间换时间。如果单例对象初始化非常快,而且占用内存非常小的时候,用这种方式是比较合适的,可以直接在应用启动时加载并初始化。
2.懒汉式,线程不安全
public class Singleton {
private static Singleton instances = null;
private Singleton() {}
//如果发现没有实例对象,就构造一个;如果有实例对象,直接返回
public static Singleton getInstances() {
if (instances == null) {
instances = new Singleton();
}
return instances;
}
public void doSomething() {
//doSomething
}
}
该 懒汉式 就是将单例的初始化操作,延迟加载 到需要的时候才进行,这样做在某些场合中有很大用处。比如某个单例用的次数不是很多,但是这个单例提供的功能又非常复杂,而且加载和初始化要消耗大量的资源,这个时候使用懒汉式就是非常不错的选择。但它在 多线程中不安全(比如,有两个线程,一个是线程 A,一个是线程 B,它们同时调用 getInstances 方法,就可能导致并发问题),是典型的时间换空间。
3.懒汉式,线程安全
public class Singleton {
private static Singleton instances = null;
private Singleton() {}
public static Singleton getInstances() {
//加入同步锁 synchronized
synchronized (Singleton.class) {
if (instances == null) {
instances = new Singleton();
}
return instances;
}
}
public void doSomething() {
//doSomething
}
}
这种是最常见的解决同步问题的一种方式,使用同步锁 **synchronized (Singleton.class) **防止多线程同时进入造成 getInstances 被多次实例化。
4.懒汉式,双重校验锁
public class Singleton {
// 对保存实例的变量添加 volitile 的修饰
private volatile static Singleton instances = null;
private Singleton(){}
public static Singleton getInstances(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instances == null){
//同步块,线程安全的创建实例
synchronized (Singleton.class) {
//再次检查实例是否存在,如果不存在才真正的创建实例
if (instances == null){
instances = new Singleton();
}
}
}
return instances;
}
public void doSomething() {
//doSomething
}
}
双重校验锁 指的是:并不是每次进入 getInstances 方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例。这是第二重检查。
双重加锁机制的实现会使用一个关键字 volatile ,它的意思是:被 volatile 修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
5.静态内部类
public class Singleton {
//类级的内部类,也就是静态类的成员式内部类,该内部类的实例与外部类的实例
//没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
private static class SingletonHolder {
//静态初始化器,由JVM来保证线程安全
private static Singleton instances = new Singleton();
}
private Singleton() {}
public static Singleton getInstances() {
return SingletonHolder.instances;
}
public void doSomething() {
//doSomething
}
}
这样实现出来的单例类就是线程安全的,而且使用起来非常简洁。
6.枚举类型单例模式
public enum Singleton{
//定义一个枚举的元素,它就是Singleton的一个实例
instance;
public void doSomething(){
// do something ...
}
}
这种方法是根据 Effective Java 书中的说法,默认枚举实例的创建是 线程安全 的.(创建枚举类的单例在 JVM 层面也是能保证线程安全的), 所以不需要担心线程安全的问题,所以理论上枚举类来实现单例模式是最简单的方式。
总结
以上就是 单例模式 的几种写法。分别是饿汉、懒汉、同步锁、双重校验锁、静态内部类和枚举。我们可以根据不同的场景选择最喜欢的一种单例模式吧!
如何使用
当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式,这些功能恰好是单例模式要解决的问题
番外阅读
参考
-- 默默的划水ing--