一、单例模式简介
单例模式是对象的创建模式,单例模式能够确保某一个类只有一个单一的实例对象存在,同时能够自行实例化并将单一的实例提供给外界调用的特点,其在项目开发中经常被用到。单例模式是设计模式中最简单的,只有一个单例类,没有其他的层次结构与抽象。该模式需要确保该类只能生成一个对象,通常是该类需要消耗太多的资源或者没有没有多个实例的理由。
二、单例模式特点
综合上面的单例的模式结构图,我们可以清楚知道单例结构是简单的,但是却存在以下特点:
1.每次从getInstance()都能返回一个且唯一的一个对象。
2.资源共享情况下,getInstance()必须适应多线程并发访问。
3.提高访问性能。
4.懒加载(Lazy Load),在需要的时候才被构造。
三、单例模式常用方式
1、懒汉式
public static Singleton getInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
这个写法我们把四点需求从上往下检测,发现第2点的时候就出了问题,假设这样的场景:两个线程并发调用Singleton.getInstance(),假设线程一先判断完instance是否为null,既代码中的line 12进入到line 13的位置。刚刚判断完毕后,JVM将CPU资源切换给线程二,由于线程一还没执行line 13,所以instance仍然是空的,因此线程二执行了new Signleton()操作。片刻之后,线程一被重新唤醒,它执行的仍然是newSignleton()操作。所以这种设计的单例模式不能满足第2点需求。
优点:延迟加载(需要的时候才去加载)
缺点: 线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。
2、加同步锁
public static Singleton getInstance() {
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
return instance;
}
比起单例A仅仅在方法中多了一个synchronized修饰符,现在可以保证不会出线程问题了。但是这里有个很大(至少耗时比例上很大)的性能问题。除了第一次调用时是执行了SingletonKerriganB的构造函数之外,以后的每一次调用都是直接返回instance对象。返回对象这个操作耗时是很小的,绝大部分的耗时都用在synchronized修饰符的同步准备上,因此从性能上说很不划算。
优点:解决了线程不安全的问题。
缺点:效率有点低,每次调用实例都要判断同步锁
3、双重检验锁
public static Singleton getInstance(Context context) {
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton(context);
}
}
}
return instance;
}
优点:在并发量不多,安全性不高的情况下或许能很完美运行单例模式
缺点:不同平台编译过程中可能会存在严重安全隐患。
4、综上所有方法,采用内部类的方式实现。因为该方式可以延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗。
/**
* Description: ${单例模式}
* author Jimmy.li
* Date: 2016-07-11
* Time: 16:47
* version V1.0
*/
public class Singleton {
private static Singleton instance = null;
private static Context mContext = null;
private Singleton(Context context){
mContext = context;
}
public static Singleton getInstance(Context context) {
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton(context);
}
}
}
return instance;
}
}
四、单例模式实战
单例模式的实现在现在大多项目中均有用到,比如需要实时网络的操作,多次开启线程中运行又会使得代码冗余,重复率太高,代码质量太低,因此采用单例模式就会使得代码简捷,使得代码质量更高。
网络的实时操作如需时刻检测登录的网络操作或其他的网络延时操作均可以用到,本篇我将通过获取手机屏幕的宽度和高度实战讲解单例模式的使用。
1.在Singleton.java内部类中声明屏幕宽高度方法。
Display display = ((WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
public int getScreenWidth(){
return display.getWidth();
}
public int getScreenHeight(){
return display.getHeight();
}
2.调用方法
int mScreenWidth = Singleton.getInstance(this).getScreenWidth();
int mScreenHeight = Singleton.getInstance(this).getScreenHeight();
textView.setText("Height:" + mScreenHeight + "\nWidth:" + mScreenWidth);
五、源码下载
Good luck!