单例模式

平时工作开发中,对面做着个4年多的java开发。每次看完我的代码,都摇摇头,你这个设计台low B了,代码都拢在了一块儿。再这样的激发下,当然也感受到了代码架构的重要性,所以自己就开始了设计模式的学习。因为自己是个菜鸟,所以我会分析的很详细,把只要有一点点疑惑的地方都搞懂,然后分享出来,希望在我进步的同时也能帮到大家把。那时候也不早了,开始入正题吧。

选择单例模式是因为在我的影响中单例模式好像是最简单的一种模式,因为其他模式不敢说有没有用,但单例模式在开发中肯定是有用过的。来看看平时用的单例是哪种。

平时使用比较多的单例模式

懒汉模式(线程不安全)

public class Singleton { 
private static Singleton instance; 
private Singleton() {}
 public static Singleton getInstance() {
 if (null == instance)
 { 
   instance = new Singleton();
 } 
return instance; 
     } 
}

这个也是我以前经常写的方式,但是这个写法最大的问题就是并发的情况下,线程不安全,比如有多个线程同时去调用getInsance()这样就可能初始化多个singleton对象。那我们来对这个方法进行改进,现在不是多线程的问题嘛,那就用synchronized 修饰呗,好改完后。

懒汉模式(线程安全)

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

这样就解决了多线程下的问题,但是带来的问题是什么呢?因为我们的锁其实就是给第一次访问时用的,访问过一次以后 instance就有值了,这个时候就算是多线程 if (null == instance)这个判断都不能通过,所以第一用过后 这个synchronized就没有意义了,而我们知道synchronized影响效率,所以这个不是很好的方式。继续改进:

双重锁定(double check)

public class Singleton { 
private static Singleton instance; 
private Singleton() {}
 public  static Singleton getInstance() {

 if (null == instance) { 
synchronized(Singleton.this){
if (null == instance){
   instance = new Singleton();
  } 
}
      } 
return instance; 
  }
}

这个时候我们来分析下流程,当多线程访问的时候,同时进到getInstance()方法中,然后判断是不是null 不是,ok继续走,有一个线程进入同步方法里面,另外的在外面等着,进去后的线程发现instance是null new出来。好,第二个线程进入,已经不等于null了,直接返回了。 第一次没有问题,然后看第二次,第二次访问时,直接在第一个判断null==insance,因为instance已经不是null了,所以直接返回。这样没进入同步方法,这样看上去我们已经解决了线程安全和效率这两大问题,看来已经完美。 但是这个地方还是有一个地方有隐患的, instance = new Singleton();这个new方法,他并非一个原子操作(这个暂时理解为 按顺序执行的操作),new的时候再虚拟机里面会有三个步骤
1、给instance分配一个内存地址
2、初始化Singleton对象。以及构造函数的一些参数
3、让singleton对象指向instance的地址(这个时候instance就不为null了)
那刚才说他不是原子操作意思就是说,他并不一定完全是这个顺序,那他也可能步骤132那当他先执行了3的时候,其实instance还是空。那如果这个时候刚好多线程又去new了,那这又线程不安全了。好,解决这个问题,能不能让他保证顺序的执行,在他没执行完这个步骤时,其他线程不能进来,violate这个修饰符,可以做这个事情,那代码变成这样了:

public class Singleton { 
private violate static Singleton instance; 
private Singleton() {}
 public  static Singleton getInstance() {

 if (null == instance) { 
synchronized(Singleton.this){
if (null == instance){
   instance = new Singleton();
  } 
}
      } 
return instance; 
  }
}

当然这个violate的效果相当于,你必须把步骤走完,其他线程才能执行,但是这个violate效果只能java5以后才可以。
这样我们从平常的代码出发,已经写出了一个近似完美的单例模式了,但是貌似代码有点多,有点复杂。 我们再来看看单例还有哪些更简单,更安全的方式。

单例的静态内部类(eagerly饿汉模式)

上代码:

public class Singleton{ 
private static final Singleton instance = new Singleton(); 
private Singleton(){} public static Singleton getInstance(){ 
return instance; } 
}

为啥叫恶汉呢,因为他在类初始化的时候就new了,而且是静态唯一的,所以也就不存在多线程的问题。当然也有问题,问题是如果我不调用getinstance方法 ,是不是instance的内存就浪费了。而且主要是我如果想传递一些参数,你自己一开始就加载好了,我就传过来了。后面这个问题挺大。

单例的内部静态类模式

相对于饿汉来的那么直接,懒汉相对就含蓄点。来,看代码:

public class Singleton { 
private static class SingletonHolder { 
private static final Singleton INSTANCE = new Singleton(); 
} 
private Singleton (){} 
public static final Singleton getInstance() { 
return SingletonHolder.INSTANCE; 
}
 }

静态内部类,木有饿汉那么饥渴,调用方法的时候才去new,没有安全性问题,只加载一次。没有性能问题,没有同步啥的,关键代码也挺少(没说最少,下面还有个最少的)。挺完美,还是蛮推荐的。

单例的枚举模式(enum)

public enum Singleton{ INSTANCE; }

我靠,这也太简单了吧,关键是他还具有单例所需要的所有优点,安全,性能。简单解释一下,其实枚举在编译完以后生成的是一个类继承enum的
上面的那个枚举类,编译后是这样的

public class Singleton extends Enum{
public static final Singleton  INSTANCE;
}

枚举默认就是线程安全的,并且他是静态final的 所以也能保证唯一,其实这种写法还有好处就是可以直接序列化,并且也不用担心被反射。

单例的问题

上面介绍了实现单例模式的方法,但是单例模式会不会被其他的机制或方法破坏掉,或者因为单例而不能实现某些功能。这个当然是有的,不然我也不写了,比如反射能破坏单例,比如序列化怎么办?下面一个个介绍,思路还是问题---解决方案。

反射带来的问题及解决方案

反射带来的问题其实很好理解,通过反射可以拿到私有的属性,方法等,这样他就可以重新new出一个对象,这个时候破坏了我们的单例。解决方案是通过抛出异常来解决,拿双重锁定举例。

public class Singleton { 
private static Singleton instance; 

private static boolean flag=false;

private Singleton() { 
synchonized(Singleton.class){ 
  if(flag==false){
  flag=true;
}else{
         throw new RuntimeException("单例模式被侵犯!");  
}
}

}
 public  static Singleton getInstance() {

 if (null == instance) { 
synchronized(Singleton.this){
if (null == instance){
   instance = new Singleton();
  } 
}
      } 
return instance; 
  }
}

在构造函数里面抛出异常,这样如果通过反射如果重新new对象的话,就会报错了。

单例的序列化

单例中实现序列化的方式是实现seriliziable接口,然后还需要readResolve方法。

public class Singleton implement Serializable { 
private static Singleton instance; 

private static boolean flag=false;

private Singleton() { 
synchonized(Singleton.class){ 
  if(flag==false){
  flag=true;
}else{
         throw new RuntimeException("单例模式被侵犯!");  
}
}

}
 public  static Singleton getInstance() {

 if (null == instance) { 
synchronized(Singleton.this){
if (null == instance){
   instance = new Singleton();
  } 
}
      } 
return instance; 
  }

 private Object readResolve() { return singleton; }//实现序列化需要的方法
}

当然为什么需要这么写,大家可以参考这个。
[单例的序列化] http://www.hollischuang.com/archives/1144

结语

对设计模式的简单总结,其实还有一些疑点我也没打开,比如有人说多classloader对于单例模式的影响,这个我也没去细看,如果有清楚的可以赐教。第一次的设计模式学习分享,参考了很多大神的文章,在此就不一一列出了,对这些默默分享的人我只能说感谢,并且自己也致力于成为这样的人。当然自己现在文章如果有错漏的地方,请不吝指出,感谢!最后给一个github上所有设计模式的例子的链接,如果有人想学习可以去参考下。
[设计模式]https://github.com/iluwatar/java-design-patterns

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容