单例模式是最简单的一个,也是比较常用的一个。所以,首先就拿它先开刷了,哈哈。
说起单例模式,有的人觉得没啥呀,不就是一个类只产生一个对象么?
是的,没错,但是不知道你有没有避过这些坑呢???
让我们一块来看一下吧。。。
一:什么是单例?有什么用?应用于什么时候?
二:经典单例模式的实现原理+类图
三:实例项目代码
四:对经典单例模式坑的优化方案
一:什么是单例?有什么用?应用于什么时候?
单利模式:确保一个类最多只有一个对象实例,供大家使用。
有些对象,我们只需要一个对象实例:线程池,缓存,硬件设备等;
(比如使用打印机:如果物理上是有一个,而new多个打印机对象,被多个人只用,这样会造成数据混乱,冲突,结果不一致等问题)
--------------那么问题来了----------
是否可以使用静态变量方式实现? -------------目的可以达到
或者程序员之间协商出个全局变量? -------------可以达到
二:经典单例模式的实现原理+类图
先上类图:
实现原理:
public class Singleton {
//声明一个静态对象
private static Singleton uniqeInstance = null;
//私有化构造方法
private Singleton() {
}
//创建获取对象方法getInstance();
public static Singleton getInstance(){
//判断对象是否创建
if(uniqeInstance ==null){
//创建对象
uniqeInstance = new Singleton();
}
//返回对象
return uniqeInstance;
}
}
三:实例项目代码
初级版:
public class ChocolateFactory1 {
// 声明封装的私有属性
private boolean empty;
private boolean boiled;
// 声明静态对象
public volatile static ChocolateFactory1 uniqueInstance = null;
// 私有化构造方法
private ChocolateFactory1() {
empty = true;
boiled = false;
}
// 创建获取对象的方法
public static ChocolateFactory1 getInstance() {
// 判断
if (uniqueInstance == null) {
// 生成对象
uniqueInstance = new ChocolateFactory1();
}
// 返回对象
return uniqueInstance;
}
// 制作流程中的添加原料的方法:
public void fill() {
if (empty) {
// 添加原料巧克力动作
empty = false;
boiled = false;
}
}
// 制作流程中的熬煮的方法:
public void boil() {
if ((!empty) && (!boiled)) {
// 煮沸
boiled = true;
}
}
// 制作流程中的切割排出巧克力的方法:
public void drain() {
if ((!empty) && boiled) {
// 排出巧克力动作
empty = true;
}
}
}
相信大伙看过之后,没啥问题啊。Are you Fuck keeding me? 别上火,真没 逗你们。
看下图哈-------
大佬们,明白了么?
别捉急,听我细说------
上面那一段,当我们面对多线程访问的时候,问题就来了,
比如一个极端情况(A线程刚刚判断完确定没有对象实例创建,正去创建,就是这会儿。卡-----自己的时间片用完了,切换到B线程进来了,一看也是要创建对象,而且创建完对象走了,招呼都没打。等到切换A线程,它还是继续自己走之前的那一步(去创建个对象)
这个流程下来,,他们俩就创建了俩对象,不安全吧,是个坑吧,咋弄啊。
四:对经典单例模式坑的优化方案
别捉急,咱有的是办法,继续看--------(方法有三)
1:加个同步锁;
// 创建获取对象的方法
// 同步锁--只准许一个进,出来之后,第二才能进
public static synchronized ChocolateFactory getInstance() {
if (uniqueInstance == null) {
//创建对象
uniqueInstance = new ChocolateFactory();
}
// 返回对象
return uniqueInstance;
}
有了同步锁,就可以避免了多个线程同时访问的情况。但是同步锁是消耗资源的,尤其是当很多很多线程很多次很多次访问。
(没闹,可能真的就有这种)那就再看看下面的方法,哈哈。
2:加急对象法;
public class ChocolateFactory {
// 声明封装的私有属性
private boolean empty;
private boolean boiled;
// 声明静态对象
public volatile static ChocolateFactory uniqueInstance = new ChocolateFactory();
// 私有化构造方法
private ChocolateFactory() {
empty = true;
boiled = false;
}
// 创建获取对象的方法
public static ChocolateFactory getInstance() {
if (uniqueInstance == null) {
//创建对象
uniqueInstance = new ChocolateFactory();
}
// 返回对象
return uniqueInstance;
}
当然,还是那句话,如果我不想使用这个类的对象,他还是给我创建好了,无奈啊。内存资源又让它给败家了,别急,咱还有招儿
3:双重检查锁;
public class ChocolateFactory {
// 声明封装的私有属性
private boolean empty;
private boolean boiled;
// 声明静态对象 volatile --针对编译器使用的,处理多线程安全问题的关键字
public volatile static ChocolateFactory uniqueInstance = null;
// 私有化构造方法
private ChocolateFactory() {
empty = true;
boiled = false;
}
// 创建获取对象的方法
public static ChocolateFactory getInstance() {
// 判断
if (uniqueInstance == null) {
// 同步锁--只准许一个进,出来之后,第二才能进
synchronized (ChocolateFactory.class) {
//再次判断
if (uniqueInstance == null) {
//创建对象
uniqueInstance = new ChocolateFactory();
}
}
}
// 返回对象
return uniqueInstance;
}
public void fill() {
if (empty) {
// 添加原料巧克力动作
empty = false;
boiled = false;
}
}
public void drain() {
if ((!empty) && boiled) {
// 排出巧克力动作
empty = true;
}
}
public void boil() {
if ((!empty) && (!boiled)) {
// 煮沸
boiled = true;
}
}
}
博主有话说:
作为菜鸟的我也是看了很多资料,在这里总结整理一个,啰里啰嗦的 ,别着急,我就是个话痨。哈哈,希望能给后面的兄弟带来帮助,也希望能有大佬带我飞,我要和太阳肩并肩。