1,单例模式

package test;/*** 单例模式测试* *2016-5-18 diaowj* */
public class Singleton {/* 持有私有静态实例,防止被引用 */private static Singleton instance = null;/* 构造方法私有化 目的是为了防止被实例化 */private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了

package test;/*** TODO* *2016-5-18 diaowj* */
public class TestThreadOfRunnable implements Runnable {public static void main(String[] args) {new Thread(new TestThreadOfRunnable(), "线程1").start();new Thread(new TestThreadOfRunnable(), "线程2").start();}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+ "   在执行  创造单例对象=" + Singleton.getInstance());}}}

结果如下:

<pre name="code" class="html">线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
线程2   在执行  创造单例对象=test.Singleton@1888759
<span style="color:#ff0000;"><strong>线程1   在执行  创造单例对象=test.Singleton@4a5ab2</strong></span>
线程1   在执行  创造单例对象=test.Singleton@1888759
线程1   在执行  创造单例对象=test.Singleton@1888759
线程1   在执行  创造单例对象=test.Singleton@1888759

但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:

[java]  view plain copy
  1. public static Singleton getInstance() {
  2. if (instance == null) {
  3. synchronized (instance) {
  4. if (instance == null) {
  5. instance = new Singleton();
  6. }
  7. }
  8. }
  9. return instance;
  10. }

似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

a>A、B线程同时进入了第一个if判断

b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();

c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:

[java]  view plain copy
  1. private static class SingletonFactory{
  2. private static Singleton instance = new Singleton();
  3. }
  4. public static Singleton getInstance(){
  5. return SingletonFactory.instance;
  6. }

实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

[java]  view plain copy
  1. public class Singleton {
  2. /* 私有构造方法,防止被实例化 */
  3. private Singleton() {
  4. }
  5. /* 此处使用一个内部类来维护单例 */
  6. private static class SingletonFactory {
  7. private static Singleton instance = new Singleton();
  8. }
  9. /* 获取实例 */
  10. public static Singleton getInstance() {
  11. return SingletonFactory.instance;
  12. }
  13. /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
  14. public Object readResolve() {
  15. return getInstance();
  16. }
  17. }

其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。也有人这样实现:因为我们只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字,也是可以的:

[java]  view plain copy
  1. public class SingletonTest {
  2. private static SingletonTest instance = null;
  3. private SingletonTest() {
  4. }
  5. private static synchronized void syncInit() {
  6. if (instance == null) {
  7. instance = new SingletonTest();
  8. }
  9. }
  10. public static SingletonTest getInstance() {
  11. if (instance == null) {
  12. syncInit();
  13. }
  14. return instance;
  15. }
  16. }

java 设计模式相关推荐

  1. java备忘录模式应用场景_图解Java设计模式之备忘录模式

    图解Java设计模式之备忘录模式 游戏角色状态恢复问题 游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态. ...

  2. JAVA 设计模式 模板方法模式

    定义 模板方法模式 (Template Method) 定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成. 模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模 ...

  3. Java设计模式之策略模式与状态模式

    一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. 好了,定义看看就完了,我知道你很烦看定义. 二.策 ...

  4. JAVA 设计模式 享元模式

    用途 享元模式 (Flyweight) 运用共享技术有效地支持大量细粒度的对象. 享元模式是一种结构型模式. 结构 图-享元模式结构图 Flyweight : 它是所有具体享元类的超类或接口,通过这个 ...

  5. java设计模式 观察者模式_理解java设计模式之观察者模式

    在生活实际中,我们经常会遇到关注一个事物数据变化的情况,例如生活中的温度记录仪,当温度变化时,我们观察它温度变化的曲线,温度记录日志等.对于这一类问题,很接近java设计模式里面的"观察者模 ...

  6. Java设计模式-七大设计原则

    Java设计模式 设计模式七大原则 设计模式的目的 编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好 代码 ...

  7. Java设计模式(备忘录模式-解释器模式-状态模式-策略模式-职责链模式)

    Java设计模式Ⅶ 1.备忘录模式 1.1 备忘录模式概述 1.2 代码理解 2.解释器模式 2.1 解释器模式概述 3.状态模式 3.1 状态模式概述 3.2 代码理解 4.策略模式 4.1 策略模 ...

  8. Java设计模式(访问者模式-迭代器模式-观察者模式-中介者模式)

    Java设计模式Ⅶ 1.访问者模式 1.1 访问者模式概述 1.2 代码理解 2.迭代器模式 2.1 迭代器模式概述 2.2 代码理解 3.观察者模式 3.1 观察者模式概述 3.2 代码理解 4.中 ...

  9. Java设计模式(代理模式-模板方法模式-命令模式)

    Java设计模式Ⅴ 1.代理模式 1.1 代理模式概述 1.2 静态代理 1.2.1 静态代理概述 1.2.2 代码理解 1.3 动态代理之JDK代理 1.3.1 动态代理之JDK代理概述 1.3.2 ...

  10. Java设计模式(装饰者模式-组合模式-外观模式-享元模式)

    Java设计模式Ⅳ 1.装饰者模式 1.1 装饰者模式概述 1.2 代码理解 2.组合模式 2.1 组合模式概述 2.2 代码理解 3.外观模式 3.1 外观模式概述 3.2 代码理解 4.享元模式 ...

最新文章

  1. Linux中的文件系统和磁盘管理
  2. 解决Mybatis启动报错: Invalid bound statement (not found)
  3. 数据结构二分法算法的步骤_数据结构与算法之算法思想:二分法搜索实现(python)...
  4. MVC、MVP和MVVC区别
  5. 9273:PKU2506Tiling
  6. 【数学】奶牛编号(jzoj 2932)
  7. linux系统in命令,Linux中的In命令
  8. 员工借款及还款场景演练
  9. 火山引擎正式发布大数据研发治理套件
  10. 【CSharp】C#中equals与==小记
  11. vmware10中开启Intel VT-x
  12. 12.GitLab System Hooks
  13. 计算机组成原理 唐朔飞 知识点,计算机组成原理知识点总结(唐朔飞版)
  14. MATLAB雾霾天气下运动目标检测
  15. 什么是PBR?pbr入门基础干货
  16. Word 2003域应用完全手册
  17. 分享回顾|我们是神经搜索少年团!
  18. NEAR官方文档翻译(三)基础(Basics)- 数据存储(Data Storage)
  19. 华为matebook13进入Bios,重装系统,切换启动顺序,选择U盘启动
  20. 2022-01-05:有四种诗的韵律分别为: AABB、ABAB、ABBA、AAAA。 比如 : 1 1 3 3就属于AABB型的韵律、

热门文章

  1. HTML5期末大作业:关于家乡景点介绍网页设计-------我的家乡金堂(9页) HTML+CSS+JavaScript
  2. 攻防世界- CRYPTO -练习区12题解
  3. 2021牛客暑期多校训练营2 F-Girlfriend
  4. 想做视频剪辑师到底累不累?
  5. 算法面试,是要考考你的基本盘
  6. 大数据之------------数据中台
  7. (ICRA 2020) Instance Segmentation of LiDAR Point Clouds
  8. Python爬取15万条《我是余欢水》弹幕,看郭京飞如何演活极丧中年人
  9. ath9K 驱动注册过程
  10. linux下删除文件夹的命令