Android设计模式-单例模式

来源:互联网 发布:淮南市大数据办公室 编辑:程序博客网 时间:2024/06/18 08:26

一个优秀的程序员,决不能只能仅仅满足于实现了功能。如何实现功能的同时,写出漂亮的、好维护的代码也是菜鸟到进阶的必修课。很多刚刚入门的程序员会抱着设计模式的教材当做武林秘籍,其实设计模式并没有那么高深,当你为了实现功能,或者产品经理反复的修改需求,又或者因为工期太紧了你打算先写出来东西有空再重构。。。最后当你面对着一大堆烂代码发愁怎么重构的时候,设计模式应该就能帮上你的大忙。一个良好的开始,好过后期反复的重构,前期挖的坑,终归还是要自己填上,建议还是在一开始实现功能的同时就能按照良好的代码习惯coding。废话少说,我们一起看看设计模式中最常用,最容易学的第一课——单例模式。

单例模式恐怕是最常见的设计模式了。首先为什么要使用单例模式?我们生活中有时候很多事情也只能是唯一的,比如一个班只有一个班长,一个公司只有一个CEO。对于我们开发中来说,我们有时候一整个应用中,也需要保证某个对象的类只有一个实例存在。又或者我们为了节约资源(例如new某个对象需要耗费非常多的资源),控制线程并发(通过同步锁避免不同的实例做同一个操作造成错误,比如同时写一个文件)。需要注意的是,如果在安卓中开启了多进程,比如webview的Activity设置了remote这个属性,使webview对应的Activity运行在其他的进程中,单例模式就失效了,在其他进程中也会有一个单例。

饿汉式

所谓饿汉可以理解为一个很饿的人迫不及待的去吃。看代码:

public class CEO {    private static final CEO SINGLETON = new CEO();    private CEO() {}    public static CEO getInstance() {        return SINGLETON;    }}

只有唯一一个CEO,首先先把构造方法设为private,这样调用的地方就无法通过new出CEO这个类的实例,只能通过CEO.getInstance这个方法获取CEO的实例,全局都只能有这一个实例。饿汉就是这个类在声明静态对象就初始化CEO这个对象了。

懒汉式

相比饿汉,懒汉顾名思义就是很懒,能不干活就不去干活,先看代码。

public class CEO {    private static CEO singleton;    private CEO() {}    public static synchronized CEO getInstance() {        if (singleton == null) {            singleton = new CEO();        }        return singleton;    }}

我们对比上面的饿汉会看到,这个懒汉在第一次getInstance的时候,才去初始化。

我们需要注意synchronized这个关键字,熟悉的朋友都知道这是一个同步锁,也就是在多线程中,保证了getInstance执行到的时候,里面的代码保证了原子性,也就能保证单例模式不出错。如果我们不加synchronized,有一种可能的情况是:现在有两个线程,线程1和线程2。当线程1执行到singleton = new CEO()的时候,在Java编译器中这句话会有三个操作,分别是,1:给singleton这个实例分配所需要的内存空间。2:调用构造方法做初始化。3:将实例的地址告诉singleton,也就是让singleton指向地址,不再为空。但是事实上,Java并不保证执行的顺序,有可能3已经执行完了,2再开始执行,这跟java的机制是有关系的,感兴趣的同学可以查阅JMM和Java编译器等相关资料。如果不凑巧,这个时候线程1执行完3,还没来得及执行2.这个时候切换到线程2上面了,线程2判断singleton == null的时候,因为singleton已经指向了内存中的一片地址不再是空,所以不再执行singleton = new CEO();,而是直接执行return singleton;,而singleton这个时候其实还没有初始化呢。最终结果就是getInstance返回一个null,单例模式就出问题了。

但是我们发现每次执行同步锁会带来一个问题,就是多线程执行getInstance整个方法都会被锁住,性能上肯定还有优化的空间,作为程序猿就是不能太知足。我们看解决方案。

public class CEO {    private static CEO singleton;    private CEO() {}    public static CEO getInstance() {        if (singleton == null) {            synchronized(CEO.class){               if (singleton == null) {                   singleton = new CEO();               }            }        }        return singleton;    }}

改进之后第一次判断为空,然后执行synchronized锁内的代码,也就是执行初始化,在同步块里面需要再判断一次是否为空,因为如果线程1在进入同步块之前判断singleton为空,正准备进入同步块但是还没有进入,这时候线程2开始执行,这时候singleton依旧为空,所以初始化CEO,然后线程1进入同步块,因为没有做判断会再执行一次初始化,单例就失效了。所以里面的判断是必须的,这叫做双重锁定,也叫做双重检查。到这似乎一切都完美了,不是么,性能也考虑到了,也避免了各种线程切换带来的失效问题。其实并不是完美的,我们接下来看。

内部静态类

public class CEO{      private CEO(){ }      public static CEO getInstance(){          return CEOHolder.instance;           }      static class CEOHolder{          private static CEO instance = new CEO();      }  } 

静态内部类只会被加载一次,这样解决了之前的全部问题,代码出奇的漂亮,推荐这种方式的单例。

即使是我们认为最简单的单例模式,也有这么多玩法,设计模式真的很神奇。

1 0
原创粉丝点击