我在看开源代码时,时不时会惊叹,这怎么写的。那么完美,层层递进,行云流水,我什么时候才能写出这样诗画般的代码。
接着我就踏上了设计模式的学习之路。设计模式很多,比如抽象工厂模式,建造者模式,装饰模式,观察者模式,适配器模式,单例模式等等
第一弹,设计模式之单例模式
单例模式是设计模式中最简单的,最简单的,最简单的
单例模式的意义:
我们现在想像一个场景 : 有一个打印机类,物理硬件就只有一个,(哈哈。我们实验室就只有一台打印机。)如果在多个地方new出实例,并且是同时执行打印操作,那么打印出来的东西,就不仅仅是你的东西了,可能上面是别人的,紧接着才是你的,也就是东西会混在一起。
这种情况,一个类的对象只需要一个,如果有多个实例会造成冲突
单例模式就是确保一个类只有一个实例,并提供一个全局访问点
可以参考下图
Singleton类,有一个static静态变量uniqueInstance;私有的构造方法,和静态的getInstance()方法
这里封闭了构造方法,那么外部就无法使用构造方法来产生实例了。接着getInstance()这个提供给外部调用的静态方法用于获取唯一的实例uniqueInstance
一个很标准的单例模式模型代码如下
public class Singleton{
private static Singleton singleton = null;
private SingleTon(){
};
public static Singleton getInstance(){
//这里先判断singleton实例是否存在,如果不存在,表明还没有创建过这个类的实例,接着就执行if语句的逻辑new出一个实例。如果已经存在,表明已经创建过实例了,依据单例模式原理,直接返回全局的静态singleton即可
if (singleton == null){
singleton = new Singleton();
}else {
return singleton;
}
}
}
接下来进行代码演示,巧克力工厂项目
public class ChocolateFactory{
//标记锅里材料是否为空
private boolean empty;
//标记是否煮沸
private boolean boiled;
public ChocolateFactory(){
empty = true;
boiled = false;
}
//填满材料
public void fill(){
if(empty){
empty = false;
boiled = false;
}
}
//倒出煮沸的巧克力
public void drain(){
if((!empty)&&boiled){
empty = true;
}
}
//煮沸
public void boid(){
if((!empty)&&(!boiled)){
boiled = true;
}
}
}
就一个大锅用来煮巧克力,假设创建了两个巧克力工厂对象,当一个对象执行装满巧克力材料方法后,另一个对象再次调用,此时empty已经被赋值为false,即锅里填满了材料了
使用单例模式之后
public class ChocolateFactory{
private boolean empty;
private boolean boiled;
private static ChocolateFactorychocolateFactory = null;
private ChocolateFactory(){
empty = true;
boiled = false;
}
public static ChocolateFactory getInstance(){
if(chocolateFactory == null){
chocolateFactory = newChocolateFactory();
}else {
return chocolateFactory;
}
}
public void fill(){
if(empty){
empty = false;
boiled = false;
}
}
public void drain(){
if((!empty)&&boiled){
empty = true;
}
}
public void boid(){
if((!empty)&&(!boiled)){
boiled = true;
}
}
}
然而,还没完呢
接下来是多线程的情况。线程是依据时间片来执行的。
看到下面这段代码
public static ChocolateFactory getInstance(){
if(chocolateFactory == null){
chocolateFactory = newChocolateFactory();
}else {
return chocolateFactory;
}
}
如果现在两个线程,第一个线程thread1的时间片到了 if(chocolateFactory == null),这里先进行判断,然后执行chocolateFactory = newChocolateFactory();刚好在执行这段代码的时候,突然间时间片切换到第二个线程thread2,thread2也是执行这段代码,因为上一个时间片的时候,new创建对象还没有完成,指针还是为空。那么这个时候还是会new出一个对象。总的下来,两个线程就获取到了两个巧克力工厂对象,这是多线程不安全性的很好的体现
方法1:加上同步锁 Synchronized关键字
public static synchronized ChocolateFactorygetInstance(){
if(chocolateFactory == null){
chocolateFactory = newChocolateFactory();
}else {
return chocolateFactory;
}
}
这时,thread1执行创建对象时,如果时间片切换到thread2,因为线程thread1持有同步锁,线程2无法执行。这是要等到时间片再次切换到线程1, thread1执行完释放出同步锁后thread2才能继续执行。同步锁消耗资源比较多。
方法2:急切创建实例
public class ChocolateFactory{
private boolean empty;
private boolean boiled;
private static ChocolateFactorychocolateFactory = new ChocolateFatory();
private ChocolateFactory(){
empty = true;
boiled = false;
}
}
看到private static ChocolateFactorychocolateFactory = new ChocolateFatory();
那么这个程序一开始运行就直接创建了这个对象,对象个数恒为1
方法3:双重检查加锁
public static ChocolateFactory getInstance(){
if(chocolateFactory == null){
synchronized(ChocolateFactory.class){
if(chocolateFactory == null){
chocolateFactory = new ChocolateFactory();
}
}
}
return chocolateFactory;
}
这个方法也很好理解,就是同步锁住了一片区域,
synchronized(ChocolateFactory.class){
if(chocolateFactory == null){
chocolateFactory = new ChocolateFactory();
}
}
较之简单粗暴的方法1,这种方法同步只会执行一次,消耗的资源比较少