`
eric_hwp
  • 浏览: 118836 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java多线程编程环境中单例模式的实现

 
阅读更多

Java多线程编程环境中单例模式的实现
关键字 多线程 单例模式

Abstract

在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只
有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用。这个机制在
single-threaded环境下的实现非常简单,然而在multi-threaded环境下却存在隐患。本文重点介绍惰性加载机制以及其在多线程环境下的使用方法。
(作者numberzero,参考IBM文章《Double-checkedlockingandtheSingletonpattern》,欢迎转载与讨论)
1单例模式的惰性加载
通常当我们设计一个单例类的时候,会在类的内部构造这个类(通过构造函数,或者在定义处直接创建),并对外提供一个static getInstance方法提供获取该单例对象的途径。例如:Java
代码
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){
        …

    }
    public static Singleton getInstance(){
        return instance;

    }
}

这样的代码缺点是:

第一次加载类的时候会连带着创建Singleton实例,这样的结果与我们所期望的不同,因为创建实例的时候可能并不是我们需要这个实例的时候。同时如果这个Singleton实例的创建非常消耗系统资源,而应用始终都没有使用Singleton实例,那么创建Singleton消耗的系统资源就被白白浪费了。为了避免这种情况,我们通常使用惰性加载的机制,也就是在使用的时候才去创建。以上代码的惰性加载代码如下:
public class Singleton{
    private static Singleton instance = null;
    private Singleton(){
        …
    }
    public static Singleton getInstance(){
        if(instance==null)
            instance=new Singleton();
        return instance;
    }
}

这样,当我们第一次调用Singleton.getInstance()的时候,这个单例才被创建,而以后再次调用的时候仅仅返回这个单例就可以了。
2惰性加载在多线程中的问题

先将惰性加载的代码提取出来:
public static Singleton getInstance(){
    if(instance==null)
        instance=new Singleton();
    return instance;
}
这是如果两个线程A和B同时执行了该方法,然后以如下方式执行:
1.A进入if判断,此时foo为null,因此进入if内
2.B进入if判断,此时A还没有创建foo,因此foo也为null,因此B也进入if内
3.A创建了一个Foo并返回
4.B也创建了一个Foo并返回此时问题出现了,我们的单例被创建了两次,而这并不是我们所期望的。

3各种解决方案及其存在的问题
3.1使用Class锁机制以上问题最直观的解决办法就是给getInstance方法加上一个synchronize前缀,这样每次只允许一个线程调用getInstance方法:
public static synchronized Singleton getInstance(){
    if(instance==null)

        instance = new Singleton();
    return instance;

}
这种解决办法的确可以防止错误的出现,但是它却很影响性能:每次调用getInstance方法的时候都必须获得
Singleton的锁,而实际上,当单例实例被创建以后,其后的请求没有必要再使用互斥机制了
3.2double-checked locking

曾经有人为了解决以上问题,提出了double-checkedlocking的解决方案


public static Singleton getInstance(){
    if(instance==null)
        synchronized(instance){
            if(instance==null)
                instance=new Singleton();

        }
        return instance;
}

让我们来看一下这个代码是如何工作的:首先当一个线程发出请求后,会先检查instance是否为null,如果不是则直接返回其内容,这样避免了进入synchronized块所需要花费的资源。其次,即使第2节提到的情况发生了,两个线程同时进入了第一个if判断,那么他们也必须按照顺序执行synchronized块中的代码,第一个进入代码块的线程会创建一个新的Singleton实例,而后续的线程则因为无法通过if判断,而不会创建多余的实例。
3/3上述描述似乎已经解决了我们面临的所有问题,但实际上,从JVM的角度讲,这些代码仍然可能发生错误。对于JVM而言,它执行的是一个个Java指令。在Java指令中创建对象和赋值操作是分开进行的,也就是说instance=new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就使出错成为了可能,我们仍然以A、B两个线程为例:
1.A、B线程同时进入了第一个if判断
2.A首先进入synchronized块,由于instance为null,所以它执行instance=newSingleton();
3.由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
4.B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
5.此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
4通过内部类实现多线程环境中的单例模式为了实现慢加载,并且不希望每次调用getInstance时都必须互斥执行,最好并且最方便的解决办法如下:
public class Singleton{
    private Singleton(){
        …
    }
    private static class SingletonContainer{
        private static Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonContainer.instance;
    }
}
JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心3.2中的问题。此外该方法也只会在第一次调用的时候使用互斥机制,这样就解决了3.1中的低效问题。最后instance是在第一次加载SingletonContainer类时被创建的,而SingletonContainer类则在调用getInstance方法的时候才会被加载,因此也实现了惰性加载

分享到:
评论

相关推荐

    简历-求职简历-word-文件-简历模版免费分享-应届生-高颜值简历模版-个人简历模版-简约大气-大学生在校生-求职-实习

    简历-求职简历-word-文件-简历模版免费分享-应届生-高颜值简历模版-个人简历模版-简约大气-大学生在校生-求职-实习 简历是展示个人经历、技能和能力的重要文档,以下是一个常见的简历格式和内容模板,供您参考: 简历格式: 头部信息:包括姓名、联系方式(电话号码、电子邮件等)、地址等个人基本信息。 求职目标(可选):简短描述您的求职意向和目标。 教育背景:列出您的教育经历,包括学校名称、所学专业、就读时间等。 工作经验:按时间顺序列出您的工作经历,包括公司名称、职位、工作时间、工作职责和成就等。 技能和能力:列出您的专业技能、语言能力、计算机技能等与职位相关的能力。 实习经验/项目经验(可选):如果您有相关实习或项目经验,可以列出相关信息。 获奖和荣誉(可选):列出您在学术、工作或其他领域获得的奖项和荣誉。 自我评价(可选):简要描述您的个人特点、能力和职业目标。 兴趣爱好(可选):列出您的兴趣爱好,展示您的多样性和个人素质。 参考人(可选):如果您有可提供推荐的人员,可以在简历中提供其联系信息。 简历内容模板: 姓名: 联系方式: 地址: 求职目标: (简短描述您的求职意

    asp代码asp.net电子书城系统设计与实现(源代码+论文)

    asp代码asp.net电子书城系统设计与实现(源代码+论文)本资源系百度网盘分享地址

    JW5116F 产品规格书pdf

    JW5116F ESOP-8 40V/3A异步降压转换器

    tick need to pass

    tick

    FAT32文件系统的数据隐写

    源代码

    YOLOv5交通标志物检测源码+训练好的模型+全部数据(高分优秀项目).zip

    YOLOv5交通标志物检测源码+训练好的模型+全部数据(高分优秀项目).zip个人97分的期末大作业,主要针对计算机相关专业的正在做课程设计和期末大作业的学生和需要项目实战练习的学习者。包含全部项目源码、该项目可以直接使用、项目都经过严格调试,下载即用确保可以运行! YOLOv5交通标志物检测源码+训练好的模型+全部数据(高分优秀项目).zip个人97分的期末大作业,主要针对计算机相关专业的正在做课程设计和期末大作业的学生和需要项目实战练习的学习者。包含全部项目源码、该项目可以直接使用、项目都经过严格调试,下载即用确保可以运行! YOLOv5交通标志物检测源码+训练好的模型+全部数据(高分优秀项目).zip个人97分的期末大作业,主要针对计算机相关专业的正在做课程设计和期末大作业的学生和需要项目实战练习的学习者。包含全部项目源码、该项目可以直接使用、项目都经过严格调试,下载即用确保可以运行! YOLOv5交通标志物检测源码+训练好的模型+全部数据(高分优秀项目).zip个人97分的期末大作业,主要针对计算机相关专业的正在做课程设计和期末大作业的学生和需要项目实战练习的学习者。包含

    pypy2.7-v7.3.0rc2-aarch64.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    超微 X11DPU-XLL主板用户手册

    超微 X11DPU-XLL主板用户手册

    android studio下载

    android studio下载

    x酒高端品鉴会策划案ss.pptx

    x酒高端品鉴会策划案ss.pptx

    pypy3.10-v7.3.12rc1-s390x.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pypy2.7-v7.3.10-linux32.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    头歌python循环结构答案.txt

    头歌python循环结构答案.txt

    杰理AC1279A 规格书

    杰理AC1042B 规格书

    案例-制造业_管理信息化项目_IT规划报告dr.ppt

    案例-制造业_管理信息化项目_IT规划报告dr.ppt

    pypy3.6-v7.1.1-linux32.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pypy3.7-v7.3.5-src.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    OrCAD Capture 小工具:放置网络线阵列

    OrCAD Capture 小工具,可以提升设计效率

    基于PHP高校网上教材征订系统的设计与实现(论文+源码)-kaic.docx

    摘 要 ABSTRACT 目 录 第1章 绪论 1.1开发背景 1.2开发意义 1.3研究内容 第2章 主要技术和工具介绍 2.1 php语言 2.2 MySQL 数据库 2.3 php技术 2.4PHP简介 第3章 系统分析 3.1可行性分析 3.1.1经济可行性 3.1.2技术可行性 3.1.3操作可行性 3.2需求分析 3.3业务流程分析 3.4数据流程分析 第4章 系统设计 4.1系统结构设计 4.2功能模块设计 4.3数据库设计 4.3.1数据库设计概述 4.3.1概念设计 4.3.2表设计 第5章 系统实现 5.1基本任务 5.2登录模块的实现 5.2.1登录实现 5.2.2管理员后台登录 5.3用户模块的实现 5.3.1添加模块及登录的实现 5.2.2留言模块的实现 5.2.3征订模块的实现 5.4管理员模块的实现 5.4.1系统用户管理模块的实现 5.4.2教材信息管理模块的实现 5.4.3公告信息管理模块的实现 第6章 系统测试 6.1测试目的 6.2测试概述 6.3单元测试 6.3.1添加测试 6.3.2登录测试 6.4集成测试 第7章 总结 致 谢

Global site tag (gtag.js) - Google Analytics