好久没写东西了,但是想着无论什么事还是要坚持自己初心要坚持的东西。写东西不能断!
对于常用的23种设计模式,这里笔者会根据自己学习和出现频率、重要程度进行学习记录吧。并且每种设计模式可能会根据暂时需求侧重学习深浅。
有很多直接把单例分成很多种,这里我就分两个大类(饿汉懒汉)在这里说啦。
单例模式(Singleton Pattern)是设计模式中最简单的模式之一,属于创建型
模式。这种设计模式主要是类的对象只有一个实例,不需要每次new 创造。而我们要做的的就是确保这个对象创建的唯一
。然后根据一些特征进行优化创建以及访问改类。
而单例模式也有很多的应用,比如很多驱动例如摄像头、打印机等等,而在javaweb中的spring有很多配置文件,掌控全局,同样也是单例的。
对于单例,主要是全局只有这么一个对象。对了它的理解,这里笔者打几个有可能不太恰当的理解,目的在于帮助理解记忆,如果有错误还请指正。
个人可能不太恰当的理解:
至于单例模式的优缺点,这里就不作详细介绍了。无非是关于性能职能
、时间
、空间
、拓展性
等方面的一些讨论。这个可以参考不同人的不同理解。本文主要记录单例模式的实现方面。而同样单例模式实现上分为饿汉式和懒汉式:
单例模式创建要求:
直接暴露
或者用静态变量的get
方法)直接创建对象,不存在线程安全问题 。对于饿汉式的实现是相对简单和容易的。在实际遇到这种类似的思想其实也很多。
理解:
感觉可能
以后会用我也买买买。买买买。这一下就买全了。但是:饿汉式的几种实现方式:
01 . 直接实例化饿汉式(简洁直观)
/**
* 饿汉式
* 直接创建实例,不管是否需要这个对象
*/
public class singleton1 {
public static final singleton1 INSTANCE = new singleton1();
private singleton1()
{}
}
02 . 枚举式(最简洁)
public enum singleton2 {
INSTANCE
}
03 . 静态代码块饿汉式(适合复杂实例化)
public class singleton3 {
public static final singleton3 INSTANCE;
static {
/***
* 有可能一些数据需要配置,需要从类加载器加载
* 例如从某xxx.properties加载某些配置信息,我们只需更改配置文件不需要更改代码
*/
INSTANCE=new singleton3();
}
private singleton3()
{}
}
我们知道饿汉式在早早把对象创建好,在执行一些其他逻辑时候不存在线程安全的问题。但是懒汉式就不一样啦,延迟创建对象可能会有线程安全问题。
01 .线程不安全(适合单线程)
/***
* 懒汉式:构造器私有化
* 静态变量保存实例
* 提供一个静态方法,获取这个实例对象
*/
public class singleton4 {
private static singleton4 instance;
private singleton4(){}
public static singleton4 getInstance()
{
if (instance==null)
instance = new singleton4();
return instance;
}
}
对于这种单线程没问题,但是如果如果两个或多个线程来同时访问instance如果都为null同时申请的时候会遇到下图等之类问题,这违背了单例模式的设计原则并且可能会对系统数据造成错误。
02 .线程安全(适用于多线程)
怎么优化上述的懒汉式单例模式呢?既然不允许多个线程这样同时访问,那么咱们给它上个锁不久行了嘛!
public class singleton5 {
private static singleton5 instance;
private singleton5(){}
public static singleton5 getInstance()
{
synchronized (singleton5.class)
{
if (instance == null)
instance = new singleton5();
}
return instance;
}
}
但是这样有啥问题呢?就是我获取这个单例对象的时候,被上锁了。你气不气?我们就是在创建这个单例时候多线程可能出现点问题,咱么获取的时候不存在线程安全问题啊。。你为啥获取也要锁??
这就好比,咱们都喜欢看美女,跟美女说话要排队一个一个来,只能一个在一个时刻,但是你看美女不需要排队啊!!!
03 .线程安全推荐版(适用于多线程)
对于上述存在的问题,咱们只需要双重判定就行了。
public class singleton6 {
private static singleton6 instance;
private singleton6(){}
public static singleton6 getInstance()
{
if(instance==null) {
synchronized (singleton6.class) {
if (instance == null)
instance = new singleton6();
}
}
return instance;
}
}
两个判断为null?为啥要两个?
instance
啦!直接不操作就行了。就这样,稍微完美的方法就这样产生了。
04 .静态内部类形式(适用于多线程)
上面的方法可能有些复杂,而静态内部类也是个好方式。主要是静态内部类和外部类不是一起加载的,并且你去调用它的时候他就会初始化,并且类加载是线程安全的,这个不需要考虑线程安全问题。当加载完之后你就可以直接拿啦。这样也能达到延迟加载的作用。
这个更详细你可以自己研究静态变量(类变量)、静态内部类等等加载顺序,研究下`static关键字。
public class singleton7 {
private singleton7(){}
private static class inner
{
private static final singleton7 instance=new singleton7();
}
public static singleton7 getInstance()
{
return inner.instance;
}
}
学习参考尚学堂单例讲解以及百科、菜鸟教程等等,有些区别但是大部分实现都是相似的,带上个人理解分享给大家。如果有问题和疏漏还请指教!