单例模式应该是设计模式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例模式的应用。

首先我们先来看一下单例模式的定义:
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
单例模式的要素:
1.私有的静态的实例对象
2.私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例)
3.公有的、静态的、访问该实例对象的方法
单例模式分为懒汉形和饿汉式
懒汉式:
应用刚启动的时候,并不创建实例,当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。(时间换空间)
优点:实例在被使用的时候才被创建,可以节省系统资源,体现了延迟加载的思想。
缺点:由于系统刚启动时且未被外部调用时,实例没有创建;如果一时间有多个线程同时调用LazySingleton.getLazyInstance()方法很有可能会产生多个实例。
例子:
publicclassSingletonClass{
//私有构造函数,保证类不能通过new创建
privateSingletonClass(){}
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
if(instance==null){
//创建本类对象
instance=newSingletonClass();
}
returninstance;
}
}
饿汉式:
应用刚启动的时候,不管外部有没有调用该类的实例方法,该类的实例就已经创建好了。(空间换时间。)
优点:写法简单,在多线程下也能保证单例实例的唯一性,不用同步,运行效率高。
缺点:在外部没有使用到该类的时候,该类的实例就创建了,若该类实例的创建比较消耗系统资源,并且外部一直没有调用该实例,那么这部分的系统资源的消耗是没有意义的。
例子:
publicclassSingleton{
//首先自己在内部定义自己的一个实例,只供内部调用
privatestaticfinalSingletoninstance=newSingleton();
//私有构造函数
privateSingleton(){
}
//提供了静态方法,外部可以直接调用
publicstaticSingletongetInstance(){
returninstance;
}
}
下面模拟单例模式在多线程下会出现的问题
/**
*懒汉式单例类
*/
publicclassLazySingleton{
//为了易于模拟多线程下,懒汉式出现的问题,我们在创建实例的构造函数里面使当前线程暂停了50毫秒
privateLazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("生成LazySingleton实例一次!");
}
privatestaticLazySingletonlazyInstance=null;
publicstaticLazySingletongetLazyInstance(){
if(lazyInstance==null){
lazyInstance=newLazySingleton();
}
returnlazyInstance;
}
}
测试代码:我们在测试代码里面新建了10个线程,让这10个线程同时调用LazySingleton.getLazyInstance()方法
publicclassSingletonTest{
publicstaticvoidmain(String[]args){
//创建十个线程调
for(inti=0;i<10;i++){
newThread(){
@Override
publicvoidrun(){
LazySingleton.getLazyInstance();
}
}.start();
}
}
}
结果:
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
生成LazySingleton实例一次!
可以看出单例模式懒汉式在多线程的并发下也会出现问题,
分析一下:多个线程同时访问上面的懒汉式单例,现在有两个线程A和B同时访问LazySingleton.getLazyInstance()方法。
假设A先得到CPU的时间切片,A执行到if(lazyInstance==null)时,由于lazyInstance之前并没有实例化,所以lazyInstance==null为true,在还没有执行实例创建的时候
此时CPU将执行时间分给了线程B,线程B执行到if(lazyInstance==null)时,由于lazyInstance之前并没有实例化,所以lazyInstance==null为true,线程B继续往下执行实例的创建过程,线程B创建完实例之后,返回。
此时CPU将时间切片分给线程A,线程A接着开始执行实例的创建,实例创建完之后便返回。由此看线程A和线程B分别创建了一个实例(存在2个实例了),这就导致了单例的失效。
解决办法:我们可以在getLazyInstance方法上加上synchronized使其同步,但是这样一来,会降低整个访问的速度,而且每次都要判断。
那么有没有更好的方式来实现呢?我们可以考虑使用"双重检查加锁"的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。我们看看具体解决代码
publicclassLazySingleton{
privateLazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("生成LazySingleton实例一次!");
}
privatestaticLazySingletonlazyInstance=null;
publicstaticLazySingletongetLazyInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(lazyInstance==null){
//同步块,线程安全地创建实例
synchronized(LazySingleton.class){
//再次检查实例是否存在,如果不存在才真正地创建实例
if(lazyInstance==null){
lazyInstance=newLazySingleton();
}
}
}
returnlazyInstance;
}
}
这样我们就可以在多线程并发下安全应用单例模式中的懒汉模式。这种方法在代码上可能就不怎么美观,我们可以优雅的使用一个内部类来维护单例类的实例,下面看看代码
publicclassGracefulSingleton{
privateGracefulSingleton(){
System.out.println("创建GracefulSingleton实例一次!");
}
//类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
privatestaticclassSingletonHoder{
//静态初始化器,由JVM来保证线程安全
privatestaticGracefulSingletoninstance=newGracefulSingleton();
}
publicstaticGracefulSingletongetInstance(){
returnSingletonHoder.instance;
}
}
说一下我在实际开发中的场景:为了程序的高效率使用多线程并发,然而是循环调用,可能导致创建线程数过多,考虑采用线程池管理,这时候创建线程池仍然是处于循环调用中,也可能导致多个线程池,这时候就考虑使用单例模式。
源代码:
publicclassThreadPoolFactoryUtil{
privateExecutorServiceexecutorService;
//在构造函数中创建线程池
privateThreadPoolFactoryUtil(){
//获取系统处理器个数,作为线程池数量
intnThreads=Runtime.getRuntime().availableProcessors();
executorService=Executors.newFixedThreadPool(nThreads);
}
//定义一个静态内部类,内部定义静态成员创建外部类实例
privatestaticclassSingletonContainer{
privatestaticThreadPoolFactoryUtilutil=newThreadPoolFactoryUtil();
}
//获取本类对象
publicstaticThreadPoolFactoryUtilgetUtil(){
returnSingletonContainer.util;
}
publicExecutorServicegetExecutorService(){
returnexecutorService;
}
}
涉及到一个静态内部类,我们看看静态内部类的特点:
1、静态内部类无需依赖于外部类,它可以独立于外部对象而存在。
2、静态内部类,多个外部类的对象可以共享同一个内部类的对象。
3、使用静态内部类的好处是加强了代码的封装性以及提高了代码的可读性。
4、普通内部类不能声明static的方法和变量,注意这里说的是变量,常量(也就是finalstatic修饰的属性)还是可以的,而静态内部类形似外部类,没有任何限制。可以直接被用外部类名+内部类名获得。
以上是我在实际开发中遇到的一些问题,部分摘自网上代码,结合实开发际案例。如有不妥,希望大家及时指出!
# JAVA多线程
# JAVA单例模式
# Java单例模式的应用示例
# JAVA实现单例模式的四种方法和一些特点
# Java单例模式的6种实现方式详解
# java单例模式使用及注意事项
# 一篇文章教你使用枚举来实现java单例模式
# java 单例模式(饿汉模式与懒汉模式)
# java设计模式之单例模式学习
# Java中单例模式的7种写法
# Java实现单例模式的五种方式总结
# 多个
# 多线程
# 我在
# 我们可以
# 本类
# 不存在
# 来实现
# 系统资源
# 是否存在
# 就可以
# 自己的
# 的是
# 这时候
# 加载
# 还没有
# 好了
# 也会
# 却是
# 都要
# 就不
相关文章:
如何处理“XML格式不正确”错误 常见XML well-formed问题解决方法
教育培训网站制作流程,请问edu教育网站的域名怎么申请?
再谈Python中的字符串与字符编码(推荐)
微网站制作教程,不会写代码,不会编程,怎么样建自己的网站?
内部网站制作流程,如何建立公司内部网站?
如何彻底删除建站之星生成的Banner?
如何在万网开始建站?分步指南解析
如何设置并定期更换建站之星安全管理员密码?
如何访问已购建站主机并解决登录问题?
建站为何优先选择香港服务器?
非常酷的网站设计制作软件,酷培ai教育官方网站?
网站制作服务平台,有什么网站可以发布本地服务信息?
太原网站制作公司有哪些,网约车营运证查询官网?
css网站制作参考文献有哪些,易聊怎么注册?
动图在线制作网站有哪些,滑动动图图集怎么做?
建站之星安装后界面空白如何解决?
详解jQuery中基本的动画方法
宝盒自助建站智能生成技巧:SEO优化与关键词设置指南
如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本
建站之星安装失败:服务器环境不兼容?
制作网站的过程怎么写,用凡科建站如何制作自己的网站?
如何通过VPS搭建网站快速盈利?
个人摄影网站制作流程,摄影爱好者都去什么网站?
视频网站app制作软件,有什么好的视频聊天网站或者软件?
建站主机是否属于云主机类型?
在线制作视频的网站有哪些,电脑如何制作视频短片?
平台云上自助建站如何快速打造专业网站?
如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?
如何用已有域名快速搭建网站?
建站之星安全性能如何?防护体系能否抵御黑客入侵?
如何用腾讯建站主机快速创建免费网站?
建站主机解析:虚拟主机配置与服务器选择指南
建站之星如何开启自定义404页面避免用户流失?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
建站主机是否等同于虚拟主机?
如何快速搭建高效可靠的建站解决方案?
相亲简历制作网站推荐大全,新相亲大会主持人小萍萍资料?
制作电商网页,电商供应链怎么做?
外汇网站制作流程,如何在工商银行网站上做外汇买卖?
建站之星五站合一营销型网站搭建攻略,流量入口全覆盖优化指南
Python文件管理规范_工程实践说明【指导】
实现虚拟支付需哪些建站技术支撑?
洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?
建站org新手必看:2024最新搭建流程与模板选择技巧
如何在景安服务器上快速搭建个人网站?
如何在Golang中引入测试模块_Golang测试包导入与使用实践
如何选择香港主机高效搭建外贸独立站?
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
建站主机类型有哪些?如何正确选型
深圳企业网站制作设计,在深圳如何网上全流程注册公司?
*请认真填写需求信息,我们会在24小时内与您取得联系。