天天看点

设计模式一:单例模式

单例模式是最简单的一个,也是比较常用的一个。所以,首先就拿它先开刷了,哈哈。

 说起单例模式,有的人觉得没啥呀,不就是一个类只产生一个对象么?

是的,没错,但是不知道你有没有避过这些坑呢???

让我们一块来看一下吧。。。

一:什么是单例?有什么用?应用于什么时候?

二:经典单例模式的实现原理+类图

三:实例项目代码

四:对经典单例模式坑的优化方案

一:什么是单例?有什么用?应用于什么时候?

单利模式:确保一个类最多只有一个对象实例,供大家使用。

有些对象,我们只需要一个对象实例:线程池,缓存,硬件设备等;

(比如使用打印机:如果物理上是有一个,而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;
		}
	}
}
           

博主有话说:

    作为菜鸟的我也是看了很多资料,在这里总结整理一个,啰里啰嗦的 ,别着急,我就是个话痨。哈哈,希望能给后面的兄弟带来帮助,也希望能有大佬带我飞,我要和太阳肩并肩。

继续阅读