Java并发编程(3)-如何构造线程安全应用

『死磕Java并发编程系列』并发编程工具类之CountDownLatch 《死磕 Java 并发编程》系列连载中,大家可以关注一波: ????????『死磕Java并发编程系列』 01 十张图告诉你多线程那些破事 『死磕Java并发编程系列』 02 面试官:说说什么是Java内存模型? 『死磕Java并发编程系列』 03 面试必问的CAS原理你会了吗? 『死磕Java并发编程系列』 04 面试官:说说Atomic原子类的实现原理? ????????『死磕Java并发编程系列』 05 图解Java中那18 把锁 在日常编码中,Java 并发编程可是少不了,试试下面这些并 阅读详情


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


到目前为止,前两篇文章已经介绍了线程安全与同步的基础知识。但是我们并不希望为了获得线程安全而去分析每次内存访问;而希望线程安全的组件能够以安全的方式组合成更大的组件或者程序。这一篇将介绍一些构造线程安全类的通用模式,这些模式让类更容易成为线程安全类。
本篇总结自《Java并发编程实践》第四章 组合对象 章节的内容 ,详情可以查阅该书。

一、实例限制模式

即使一个对象不是线程安全的,仍然有许多技术可以使它成为安全的多线程程序。比如,你可以确保它只被单一的线程所访问(上篇讲的线程限制技术),也可以确保所有的访问都正确地被锁保护。

1.1、 限制变量确保线程安全

将数据封装在对象内部,把对数据的访问限制在对象的方法上,更容易确保线程在访问数据时总能获得正确的锁。如下面的程序:

public class PersonMap{
	//persons是一个有状态的对象,每个线程访问该变量可能都不同
	private final Map persons = new HashMap<String,Person>();
	
	//为添加Person的方法上锁
	public synchronized Map addPerson(String personName,Person person){
		return persons.put(personName,person);
	}
}

可以看出,persons是一个有状态的变量,有状态的变量通常是导致类线程不安全的因素,可是这个PersonMap类是线程安全的,这是因为在这个类中,线程需要改变变量的唯一方法是进入addPerson这个方法,但是这个方法是被synchronized关键字上锁的,所以唯一的入口已经安全,那么整个类都是安全的。
在这里插入图片描述

1.2、分析ArrayList的线程安全性

ArrayList的主要源码:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { /**
     * 列表元素集合数组
     * 如果新建ArrayList对象时没有指定大小,那么会将EMPTY_ELEMENTDATA赋值给elementData,
     * 并在第一次添加元素时,将列表容量设置为DEFAULT_CAPACITY 
     */ transient Object[] elementData; /**
     * 列表大小,elementData中存储的元素个数
     */ private int size; }

所以通过这两个字段我们可以看出,ArrayList的实现主要就是用了一个Object的数组,用来保存所有的元素,以及一个size变量用来保存当前数组中已经添加了多少元素。其中的elementData和size两个变量是有状态变量,且没有任何的处理,接下来看看add方法:

public boolean add(E e) { /**
     * 添加一个元素时,做了如下两步操作
     * 1.判断列表的capacity容量是否足够,是否需要扩容
     * 2.真正将元素放在列表的元素数组里面
     */ ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }

当2个线程共同进入add方法时,例如当前size为5,那么线程1访问到size为5时,ensureCapacityInternal()方法对size进行扩容,将size扩容为6;在这个时候线程2读取到的size是6,已经可以插入数据,但是这个数据已经被线程1插入了,所以产生了线程的不安全。那应该怎么解决呢?我们可以限制变量确保线程安全,如下列代码:

public synchronized boolean add(E e) { /**
     * 添加一个元素时,做了如下两步操作
     * 1.判断列表的capacity容量是否足够,是否需要扩容
     * 2.真正将元素放在列表的元素数组里面
     */ ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
1.3、总结

限制性使构造线程安全的类变得更容易。因为类的状态被限制后,分析它的线程安全性时,就不必检查完整的查询。

二、委托线程安全模式

2.1、什么是委托线程安全

我们还可以将线程的安全性委托给多个状态变量,只要这些变量是彼此独立的,即组合而成的类并不会在其包含的多个状态变量上增加任何不变性条件。

2.2、委托线程安全的实例
public class StatefulServlet extends HttpServlet{
    //使用concurent并发包中的atomic工具包下的原子变量类,保证多线程请求下的原子操作
    private AtomicLong count = new AtomicLong(0);
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String paramName = servletRequest.getParameter("paramName");
        count.incrementAndGet();//自增1
        servletResponse.getWriter().write(paramName+count);
    }
}

这就是个很好的委托线程安全模式,即把整个类的线程安全状况委托给了这个count变量,由于count变量采用的了原子变量类,所以这个委托是生效的,该类也线程安全。

三、基于线程安全类的扩展模式

Java类库中包含了许多有用的“构建块”类。重用这些已有的类要好与创建一个新的类。重用能够降低开发的难度、风险以及维护成本。有时一个线程安全类支持我们需要的全部操作,但更多的时候,一个类只支持我们需要的大部分操作,这时需要在不破坏其线程安全性的前提下,向它添加一个新的操作。这就是基于线程安全类的扩展模式。

3.1、基于Vector的功能扩展

添加一个新原子操作的最安全的方式是,修改原始的类,以支持期望的操作。但是你可能无法访问源代码或者修改的自由,所以通常需要扩展类。

public class BetterVector<E> extends Vector<E>{
public synchronized boolean putIfAbsent(E x){
	boolean absent = !contains(x);
		If(absent){
			add(x);
		}
		return absent;
}
}
3.2、基于List的组合

向已有的类中添加一个原子操作,还有更健壮的选择:组合。下列代码中,ImprovedList通过将操作委托给底层的List实例,实现了List的操作,同时还添加了一个原子的putIfAbsent方法。

public class ImproveList<T> extends List<T>{
	private final List<T> list;
	
	public ImproveList(List<T> list){
		this.list = list;
	}
	
	public synchronized boolean addIfAbsent(T x){
		if(contains){
			list.add(x);
			return true;
		}
		return false;
	}
	
}

在这里插入图片描述
通过内部锁,ImproveLis引入了一个新的锁层。它并不关系底层的List是否线程安全,即使List不是线程安全的,虽然额外的一层同步可能会带来一些微弱的性能损失,但是对于并发程序来说,应该永远坚持:宁可牺牲时间,也要保证安全!

Java中线程安全问题 。。。 阅读详情

相关推荐

Java并发编程

Java并发编程是Java语言中一个重要且复杂的领域,涉及多线程、线程安全、同步机制、并发工具类等多个方面。

GanKuRenSheng的博客 1150

【并发编程】线程安全性问题

线程安全性问题

互联网小阿祥 825

CosyVoice 本地部署详细教程 手把手小白教程

本文提供了详细的CosyVoice本地部署教程,强调了避免中文名称和空格的重要性,并列出了必要的软件安装步骤,包括CUDA、cuDNN、Git和Miniconda。教程涵盖了从检查CUDA版本到下载模型和运行模型的全过程,确保用户能够顺利部署并使用CosyVoice。

huazn612的博客 2万+

并发编程——创建线程安全的类,对象组合

1.1 实例封闭 我们时常需要创建一个线程安全的类,那么第一步,收集同步需求,并将同步策略写成正式文档。例如:1.状态变量的上届和下届值,范围;2.依赖状态的操作:比如某些方法中,包含一些先验条件,再删除某个元素前,队列必须处于非空状态。 如果某个对象不是线程安全的,那么可以通过多种技术在多线程程序中安全地使用。我们可以确保该对象智能由单个线程访问(线程封闭),或者通过一个锁来保护对该对象的所...

qq_36120793的博客 846

避免踩坑!杰理AC63蓝牙名修改常见问题及解决方案(含BLE后缀处理)

本文详细解析了杰理AC63蓝牙设备名称修改的全过程,包括基础操作、BLE后缀处理及高级调试技巧。针对蓝牙名修改中的常见问题,提供了实用的解决方案和代码示例,帮助开发者避免踩坑并实现高效定制。

q6r7s8t9的博客 413

泛微Ecology9单点登录实战指南:从Token获取到页面跳转

本文提供了一份详尽的泛微Ecology9单点登录实战指南,重点解析了Token跳转式SSO的核心流程。内容涵盖从理解关键参数(如appid、loginid)与一次性Token机制,到通过代码示例演示如何正确调用接口获取Token,并详细说明了拼接不同场景跳转地址的完整规则与安全实践,旨在帮助开发者高效、稳定地实现业务系统与OA门户的无缝集成。

ol789012345的博客 1075

深入理解并发编程之线程安全

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

李永志的博客 2666

QT与Coin3D实现机器人的仿真

前言 Coin3D编程环境配置好后,开始下一步的机器人仿真任务。本文根据在研5轴机器人,完成三维模型生成与机器人的仿真。主要参考博主冯Jungle的博客,在此表示感谢。 ——2021.05.23 环境 Win10:VS2019、QT5.15.2、Coin3D、SolidWorks2016 机器人三维模型生成 目前广泛应用于因特网的VRML ( Virtual Reality Modeling Language,虚拟现实建模语言)为用户提供一种可参与的、并能对场景中对象主动做出反应的虚拟现实环境,现已成

焦露的博客 6908

并发编程(十六)-CompletableFuture原理与实践

并发编程(十六)-CompletableFuture原理与实践

及时当勉励,岁月不待人 2557

奈曼-皮尔逊准则

奈曼-皮尔逊准则 如果各个假设的先验概率P(Hi)(i=0,1)P(H_i)(i=0,1)P(Hi​)(i=0,1)已知,并且每种判决的代价函数已知,这时,可以用贝叶斯准则进行判决。 如果进一步假设正确判决不付出的代价,而错误判决代价相同,即c00=c11=0,c01=c10=1c_{00}=c_{11}=0,c_{01}=c_{10}=1c00​=c11​=0,c01​=c10​=1,这时可以用最小平均错误概率准则进行判决。 在不知道先验概率P(Hi)(i=0,1)P(H_i)(i=0,1)P(Hi​)(

little_soldier的专栏 1万+

高通引导文件获取全攻略:ELF/MBN提取与EDL救砖实战

在智能手机底层维护中,引导加载程序(Bootloader)是系统启动的第一道关卡。高通平台通过PBL、SBL1/XBL等阶段完成硬件初始化和安全校验,一旦引导分区损坏,设备便可能陷入黑砖状态。此时,掌握ELF与MBN格式的引导镜像获取方法,成为维修工程师和刷机用户的核心技能。通过官方固件包解包或借助EDL模式直接从设备分区备份,均能获得原厂匹配的引导文件。配合Firehose工具和9008端口通信,即可实现分区级读写与救砖恢复。本文从启动链路原理出发,对比两种获取路径的适用场景,并针对驱动识别、文件校验、分

weixin_30175731的博客 428

JAVA并发编程实战

网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击“普通下载”) 购买正版 封页 编辑推荐 第16届Jolt大奖提名图书   JavaOne大会*畅销图书   了解Java并发编程必读佳作 内容简介 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。...

CiCi岛的专栏 541

YOLOv8中Bottleneck模块详解

Bottleneck 模块在YOLOv8中的作用是进行特征提取和增强,是网络中的核心构建模块之一。它的主要功能是通过卷积操作来处理输入特征图,并在适当情况下应用残差连接,使得信息能够有效地通过网络层进行传播

zzzyyy8的博客 8066

全面详解Java并发编程:从基础到高级应用

Java并发编程提供了强大的工具和框架,帮助开发者有效管理多线程任务,提升应用程序性能。理解并掌握基本概念、线程管理、同步机制和高级并发工具,并遵循最佳实践,可以编写出高效、安全、可靠的并发程序。

微笑听雨 1万+

大学计算 - NumPy库的矩阵运算 实训答案

本文介绍了三个NumPy矩阵操作的实验练习。第一关练习ndarray类型操作,包括矩阵构造、元素平方和计算、矩阵乘积运算和逆矩阵行列式计算;第二关针对matrix类型,涉及绝对值最大值获取、特定矩阵乘积运算和转置矩阵特征值计算;第三关实现Moore-Penrose伪逆矩阵计算,并与NumPy内置pinv函数结果比较误差。实验涵盖了NumPy核心矩阵操作,展示了从基础运算到高级矩阵分解的应用,帮助掌握科学计算中的矩阵处理技巧。

2403_89314916的博客 311

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Java并发编程中线程安全如何实现?

本文系统总结了Java并发编程中实现线程安全的技术方案。首先阐述了线程安全对数据完整性和原子操作的重要性,然后详细介绍了三大实现手段:内存隔离技术(包括对象内部锁和CAS原子操作)、锁机制(synchronized关键字和ReentrantLock)以及代码优化策略(简化同步、合理设计锁粒度等)。最后通过电商订单处理案例,展示了如何综合运用这些技术确保多线程环境下的数据一致性和系统稳定性。文章强调应根据实际需求选择合适方案,并始终关注多线程环境下的数据正确性问题。

2501_90410679的博客 41

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Java并发编程必知的9大线程安全问题

Java并发编程中常见的线程安全问题包括竞态条件、可见性问题、死锁、脏读、活锁、资源饥饿、发布逸出、集合类不安全以及原子性破坏等。这些问题主要发生在多线程共享资源且未正确同步时,可能导致数据不一致、死循环或程序崩溃。解决方案包括使用同步锁机制、原子变量、线程安全容器、volatile关键字、ThreadLocal以及不可变对象等。最佳实践是优先使用java.util.concurrent包提供的并发工具,避免手动实现同步逻辑。理解这些问题及其解决方法是开发高并发安全程序的关键。

2301_80215285的博客 1305

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java非高并发数据保证数据线程安全【学习笔记3】

在Java后端开发中,由于每接收一个请求,tomcat都会分配一条线程去执行请求,因此Java后端服务器的开发天然就处于一个多线程的环境,因此并发数据安全一直都是一个绕不开的话题。本文将对非高并发数据的数据安全进行设计讲解(高并发数据可以使用Redis),给出几种可行的方案。

weixin_62864469的博客 709

Java 并发编程实战

并发编程是现代软件开发的核心技能,其本质可归纳为三个关键问题:分工、同步和互斥。分工关注如何高效分配任务给线程;同步解决线程间的协作问题;互斥确保共享资源的安全访问。学习并发编程需要"跳出来看全景"建立知识体系,同时"钻进去看本质"理解底层理论模型。Java SDK提供的并发工具如Executor、Future、锁机制等,都是对这些核心问题的解决方案。掌握这些概念不仅能提升程序性能,更能保障多线程环境下的正确性。建议学习者通过思维导图梳理知识脉络,并结合实际场景深入理解管程、内存模型等基础理论。

刘大猫 2084

Java集合中线程安全的类

集合中线程安全的类都是jdk1.1中的出现的。在jdk1.2之后,就出现许多非线程安全的类。 下面是这些线程安全的同步的类: vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用 statck:堆栈类,先进后出 hashtable:就比hashmap多了个线程安全 enumeration:枚举,相当于迭代器 ...

仅做个人笔记 5690

【并发编程】Java并发编程进阶

本文介绍了CAS操作以及Java中实现的原子类,还有JUC中一些重要的类和属性

telasi2004的博客 1215

Java 并发编程之AQS

AbstractQueuedSynchronizer (AQS) 是 Java 并发编程中的一个核心框架,广泛用于构建锁和其他同步器(如信号量、读写锁等)。它是 java.util.concurrent.locks 包的一部分。AQS 的设计目的是简化并发同步器的实现。理解 AQS 对于深入理解 Java 并发编程非常重要。

微笑听雨 6764

java并发编程

什么是java并发编程如何进行并发编程

m0_64021225的博客 1724

如何学习Java并发编程

2012年我刚转行到互联网,那年夏天我从传统通信行业跳槽到互联网行业。那是我成年后看的第一部国产动画片,可谓惊为天人。里面有所谓的八奇技,其中有一门绝技称为:术之尽头,炁体源流。虽然目前作者还没有揭开谜底,但从定义上来说,炁体源流大概有“追本溯源,回归本质”的含义。为什么扯这些呢?很多人学不好Java并发编程,恰恰是因为没有抓住本质。

smart_an的专栏 1485

技术分享java并发编程

Java并发编程 1.什么是java并发编程 ​ Java是一种多线程编程语言,我们可以使用Java来开发多线程程序。 多线程程序包含两个或多个可同时运行的部分,每个部分可以同时处理不同的任务,从而能更好地利用可用资源,特别是当您的计算机有多个CPU时。多线程使您能够写入多个活动,可以在同一程序中同时进行操作处理。 根据定义,多任务是当多个进程共享,如CPU处理公共资源。 多线程将多任务的概念扩展到可以将单个应用程序中的特定操作细分为单个线程的应用程序。每个线程可以并行运行。 操作系统不仅在

superzc的博客 2422
上一篇: Java并发编程(2)-线程安全之共享对象解读
下一篇: Java并发编程(4)-同步容器和并发容器
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值