论坛首页
论坛首页
模板
模板
课程
学院课程
产品手册
产品手册
开发者
开发者中心
回到官网
回到官网
我的课程
伙伴云客服论坛
»
论坛
›
S区
›
S软件开发
›
查看内容
0 评论
0 收藏
分享
JAVA多线程并发下的单例形式应用
萌晓二
楼主
发布于 2023-4-19 14:13:15
阅读 727
查看全部
搜更多:
百度
谷歌
360
搜狗
搜搜
有道
必应
即刻
单例形式应该是设计形式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例形式的应用。
首先我们先来看一下单例形式的定义:
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
单例形式的要素:
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多线程并发下的单例形式应用
搜更多:JAVA多线程并发下的单例形式应用
搜更多:JAVA多线程并发下的单例形式应用
搜更多:JAVA多线程并发下的单例形式应用
搜更多:JAVA多线程并发下的单例形式应用
搜更多:JAVA多线程并发下的单例形式应用
回复
举报
使用道具
分享
上一篇:
浅谈java中的对象、类、与方法的重载
下一篇:
C/C++中#define的妙用分享
全部回复
暂无回帖,快来参与回复吧
返回列表
发新帖
回复
本版积分规则
高级模式
B
Color
Image
Link
Quote
Code
Smilies
发表回复
回帖后跳转到最后一页
萌晓二
注册会员
主题
21
回复
20
粉丝
0
加好友
发私信
热点排行
1
如何搭建伙伴云系统,看了你就懂
2
重要 | 伙伴云送福利啦!2023年价格体系全
3
使用技巧分享: 巧用伙伴云OpenAPI SDK,提
4
东鹏瓷砖: 跑赢疫情,只因抓住这一点
5
学会不求人哦~ 计算字段新增函数使用技巧
6
用扫一扫自动添加发票信息
标签
PMP
销售
管理
零代码
产品
云表格Pro
项目协作
零代码aPaaS
OKR
产品更新
解决方案
CRM客户关系管理
任务管理
进销存管理
售后管理
项目管理
学习资料
模板中心
伙伴学院
产品手册
客户案例
资料下载
开发者中心
关于我们
公司介绍
最新活动
媒体报道
BBS论坛
进一步了解
价格
全流程定制
企业微信
友情链接
取得联系
咨询专线
400 006 1585
关注服务号
微信交流群
服务协议
隐私政策
Cookie条款
© 2022 伙伴智慧(北京)信息技术有限公司
京公网安备11010802025927号
京ICP备12038259号
增值电信业务经营许可证:京B2-20201325
Created with
Huoban.
|
网站地图
快速回复
返回顶部
返回列表