「干货分享」经典设计模式之单例模式
Posted 似水流年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「干货分享」经典设计模式之单例模式相关的知识,希望对你有一定的参考价值。
设计模式千千万,总是单例最常见。
单例模式的定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
六种单例的创建方式
1.饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优点:
基于类的加载机制,避免了多线程同步问题,加载速度快。
缺点:
在类加载的时候就完成初始化,没有懒加载,如果没有使用这个实例,会造成内存浪费。
2.懒汉式-线程不安全版
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
优点:
第一次调用是才初始化对象,避免浪费资源
缺点:
加载速度慢,线程不安全
3.懒汉式-线程安全版(synchronized加锁)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
优点:
多线程中保证线程安全
缺点:
每次获取对象实例,都需要进行同步,造成不必要的同步开销。
4.双重校验锁
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance == null){
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优点:
线程安全,懒加载,减少同步开销
缺点:
第一个获取对象速度稍慢,但其在某些情况下也会出现失效的情况,并不是完美的方式。
这里面使用了两次判空:第一次为了不必要的加锁同步,第二次是确保在instance为null的情况下才创建实例,避免多次创建。
方法中还是用了关键字volatile对变量进行修饰,有如下几个作用:
1.在Java内存模型中volatile可以保证可见性,及防止程序指令重排序。
2.对象的创建分为如下几个步骤:
instance = new Singleton();
- 1.为instance分配内存空间
- 2.初始化instance
- 3.将instance指向内存地址
如果不加volatile的话,程序的执行顺序就可能变成1->3->2,多线程中就会导致线程获取一个没有初始化的实例。例如线程a 执行了1,3, 此时线程b调用getInstance()发现instance不为空,返回instance,但此时instance还未初始化。
5.静态内部类
public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
第一次加载类的时候不会初始化instance,只有第一次调用getInstance()的时候才会进行加载SingleHolder并初始化instance,保证线程安全,也能保证实例唯一,推荐使用这种方式。
6.枚举
public enum Singleton {
INSTANCE;
public void doSomeThing(){}
}
默认枚举单例的创建是线程安全的,并且任何情况下都是单例。
以上就是6中常见的单例创建形式,按需使用吧。
单例的使用场景
- 整个项目需要一个共享访问点或者数据
- 创建一个对象需要耗费的资源太多,比如访问数据库资源等
- 工具类对象
以上是关于「干货分享」经典设计模式之单例模式的主要内容,如果未能解决你的问题,请参考以下文章