java对象组合_java并发编程(三): 对象的组合

对象的组合:

如何将现有的线程安全组件,组合成我们想要的更大规模的程序。

设计线程安全的类:

设计线程安全类的三个要素:

1.找出构成对象状态的所有变量;

2.找出约束状态变量的不变性条件;

3.建立对象状态的并发访问管理策略。

收集同步需求:

如果不了解对象的不变性条件与后验条件,那就不能确保线程安全性。要满足在状态变量的有效值或状态转换上的各种约束条件,就需要借助原子性和封装性。

依赖状态的操作:

如果在某个操作中包含有基于状态的先验条件,那么这个操作就称为依赖状态的操作。如在操作前,判断当前状态是否可以进行当前操作。

状态的所有权:

所有权与封装性总是相互关联的:对象封装它拥有的状态,即对它封装的状态拥有所有权,当然所有权可以通过传递对象,变成共享所有权。

实例封闭:

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。

/**

* 这里将mySet实例封闭在PersonSet中,

* 尽管HashSet是非线程安全类,

* 由于mySet是私有且不会逸出的,

* 我们通过公共接口提供给外部访问,但加上了PersonSet内置锁保护synchronized,

* 因而PersonSet是一个线程安全的类

*/

@ThreadSafe

public class PersonSet {

private final Set mySet = new HashSet<>();

public synchronized void addPerson(Person p){

mySet.add(p);

}

public synchronized boolean containsPerson(Person p){

return mySet.contains(p);

}

}

封闭机制更易于构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性时就无须检查整个程序。

Java监视器模式:

Java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。如Vector, Hashtable等类;

我们也可以通过私有锁来代替内置锁:

public class PrivateLock {

private final Object lock = new Object();

public void methodOne(){

synchronized(lock){

// do sth.

}

}

}

线程安全性的委托:

多个线程安全的类组合成的类,不一定就是线程安全的。

/**

* 委托的PersonSet

* 将内部操作委托给线程安全的类SynchronizedSet

* 从而自身也是线程安全的

*/

@ThreadSafe

public class DelegatingPersonSet {

private final Set mySet =

Collections.synchronizedSet(new HashSet());

public void addPerson(Person p){

mySet.add(p);

}

public boolean containsPerson(Person p){

return mySet.contains(p);

}

}

独立的状态变量:

我们还可以将线程安全性委托给多个状态变量,只要这些状态变量彼此独立(不相关):

/**

* 将线程安全性委托给多个彼此独立的状态变量

* VisualComponent使用CopyOnWriteArrayList(线程安全)来保存监听器列表

* keyListeners, mouseListeners彼此独立

* 因此VisualComponent线程安全

*/

public class VisualComponent {

private final List keyListeners =

new CopyOnWriteArrayList<>();

private final List mouseListeners =

new CopyOnWriteArrayList<>();

public void addKeyListener(KeyListener keyListener){

keyListeners.add(keyListener);

}

public void removeKeyListener(KeyListener keyListener){

keyListeners.remove(keyListener);

}

public void addMouseListener(MouseListener mouseListener){

mouseListeners.add(mouseListener);

}

public void removeMouseListener(MouseListener mouseListener){

mouseListeners.remove(mouseListener);

}

}

当委托失效时:

当类内部多个状态变量,他们之间存在不变性条件,即使这些状态变量各自是线程安全的,那么该类不一定就线程安全:

/**

* NumberRange不足以保护它的不变性条件

* 并发环境下不安全

*/

@NotThreadSafe

public class NumberRange {

//不变性条件: lower <= upper

private final AtomicInteger lower = new AtomicInteger();

private final AtomicInteger upper = new AtomicInteger();

public void setLower(int i){

if (i > upper.get()){ //不安全的检查

throw new IllegalArgumentException("lower can't > upper");

}

lower.set(i);

}

public void setUpper(int i){

if (i < lower.get()){ //不安全的检查

throw new IllegalArgumentException("lower can't > upper");

}

upper.set(i);

}

}

如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。

发布底层的状态变量:

如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量,例如发布上面VisualComponent的keyListeners, mouseListeners。

在现有的线程安全类中添加功能:

通过扩展类,来添加功能。

/**

* 通过扩展实现非重复Vector

*/

public class NoRepeatVector extends Vector {

public synchronized boolean putIfAbsent(E e){

boolean exist = contains(e);

if (!exist)

add(e);

return exist;

}

}

客户端加锁机制:

客户端加锁:对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户端代码。

/**

* 这段客户端代码看似线程安全,

* 但其实并不安全,因为锁住的对象不正确,

* 这里仅是锁住ListHelper对象,但list对象并没有被锁住,

* 其他客户端仍可在不安全情况下对list进行操作

*/

@NotThreadSafe

public class ListHelper {

public List list =

Collections.synchronizedList(new ArrayList());

public synchronized boolean putIfAbsent(E x){

boolean absent = !list.contains(x);

if (absent)

list.add(x);

return absent;

}

} 所以上面的代码,我们应该对list加锁,而不是ListHelper对象:

@ThreadSafe

public class SafeListHelper {

public List list =

Collections.synchronizedList(new ArrayList());

public boolean putIfAbsent(E x){

synchronized (list) {

boolean absent = !list.contains(x);

if (absent)

list.add(x);

return absent;

}

}

}

组合:

当为现有的类添加一个原子操作时,有一种更好的方法:组合(Composition)。

/**

* 通过组合实现"若没有则添加" 下午4:48:42

*/

@ThreadSafe

public class improvedList implements List {

private final List list;

public improvedList(List list) {

this.list = list;

}

public synchronized boolean putIfAbsent(T t){

boolean absent = !list.contains(t);

if (absent)

list.add(t);

return absent;

}

@Override

public synchronized int size() {

return list.size();

}

...

}

将同步策略文档化:

在文档中说明客户代码需要了解的线程安全性保证,以及代码维护人员需要了解的同步策略。

不吝指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520245.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习目标检测系列:一文弄懂YOLO算法|附Python源码

在之前的文章中&#xff0c;介绍了计算机视觉领域中目标检测的相关方法——RCNN系列算法原理&#xff0c;以及Faster RCNN的实现。这些算法面临的一个问题&#xff0c;不是端到端的模型&#xff0c;几个构件拼凑在一起组成整个检测系统&#xff0c;操作起来比较复杂&#xff0c…

Keepalived时主备负载均衡器都有VIP的问题:VRRP协议问题

文章目录一、企业案例背景二、异常现象三、分析结论解决思路3.1. 分析结论3.2. 解决思路四、SElinux 处理4.1. 查看SElinux的状态&#xff1a;4.2. 关闭SElinux五、Firewalld防火墙配置5.1. 开启vrrp 协议5.2. 重新启动防火墙5.3. 查看启动日志5.4. 虚拟ip验证5.5. 停止master节…

数据不足,如何进行迁移学习?

现在&#xff0c;人工智能的发展处于跳跃式阶段&#xff0c;我们也对AI在大型数据集的应用进展感到吃惊。更重要的是&#xff0c;那些我们没有跟踪的数十亿张照片或餐厅的评论并没有被遗漏掉&#xff1a;迁移学习技术让收集数据变得更加“容易”。另外&#xff0c;得益于PyTorc…

MongoDB+阿里云 最新数据库独家上线!

近日&#xff0c;开源数据库厂商MongoDB与阿里云在北京达成战略合作&#xff0c;作为合作的第一步&#xff0c;最新版MongoDB 4.2数据库产品正式上线阿里云平台。 目前阿里云成为全球唯一可提供最新版MongoDB服务的云厂商&#xff0c;双方合作打通了企业在云上使用最新版开源数…

一天超2000次,阿里如何打响音视频超时空战役?

在阿里&#xff0c;音视频会议已经成为跨地区沟通、开会以及招聘的首选方式。据悉&#xff0c;目前阿里巴巴的办公网络与音视频会议已经覆盖全球33个国家和地区&#xff0c;其中&#xff0c;音视频会议在过去3个月平均每天召开超过2000余场。在使用如此频繁、覆盖面如此之广的音…

java pppoe_PPPoE拨号流程

PPP会话的建立&#xff0c;需要两端的设备都发送LCP数据包来配置和测试数据通信链路。用户主机与接入集中器根据在发现阶段所协商的PPP会话连接参数进行PPP会话。一旦PPPoE会话开始&#xff0c;PPP数据就可以以任何其他的PPP封装形式发送。所有的以太网帧都是单播的。PPPoE会话…

Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用)

背景&#xff1a; 在flutter的业务开发过程中&#xff0c;flutter侧会逐渐丰富自己的路由管理。一个轻量的路由管理本质上是页面标识&#xff08;或页面路径&#xff09;与页面实例的映射。本文基于dart注解提供了一个轻量路由管理方案。 不论是在native与flutter的混合工程&…

正则表达式简单语法及常用正则表达式

基本符号&#xff1a; ^ 表示匹配字符串的开始位置 (例外 用在中括号中[ ] 时,可以理解为取反,表示不匹配括号中字符串) $ 表示匹配字符串的结束位置 * 表示匹配 零次到多次表示匹配 一次到多次 (至少有一次) ? 表示匹配零次或一次 . 表示匹配单个字符 | 表示为或者,…

用PyTorch创建一个图像分类器?So easy!(Part 1)

经过了几个月的学习和实践&#xff0c;我完成了优达学城网站上《Python Programming with Python Nanodegree》课程的学习&#xff0c;该课程的终极项目就是使用Pytorch为102种不同类型的花创建一个图像分类器。 在完成这个项目的过程中&#xff0c;我和其他学员一样&#xff…

正则表达式中的开头和结尾

^  表示匹配 以 后一个字符开头有两个作用1&#xff09;匹配以指定字符开头  ^[a-zA-Z_]\w  必须以小写字母、大写字母、下划线开头2&#xff09;用在[]内部用于取反  [^hz]    匹配不含有hz的字符$  表示匹配以 前一个字符结尾\d$  以数字结尾

阿里上市,四大洲8个国家的十位代表敲锣;全球首款支持5G双卡双待的芯片发布;撕裂者3990X:桌面史上第一次64核128线程……...

关注并标星星CSDN云计算速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周两次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go govivo X30新细节曝光&#xff1a;搭载潜望式超远摄支…

支付宝客户端架构分析:自动化日志收集及分析

小蚂蚁说&#xff1a; 《支付宝客户端架构解析》系列将从支付宝客户端的架构设计方案入手&#xff0c;细分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志收集”、“RPC 组件设计”、“移动应用监控、诊断、定位”等具体实现&#xff0c;带领…

java 快速生成有残午餐_写出优质Java代码的4个技巧

译者注&#xff1a;如果现在要求对你写的Java代码进行优化&#xff0c;那你会怎么做呢&#xff1f;作者在本文介绍了可以提高系统性能以及代码可读性的四种方法&#xff0c;如果你对此感兴趣&#xff0c;就让我们一起来看看吧。以下为译文。本文我们将介绍一些有助于解决常见问…

在Kubernetes上运行区块链服务(BaaS)

本文是在2018年11月15日由Linux基金会CNCF主办的KubeCon & CloudNativeCon China 2018大会的“Running Blockchain as a Service (BaaS) on Kubernetes”演讲内容基础上整理而成&#xff0c;从技术上介绍了阿里云如何将基于区块链Hyperledger Fabric的BaaS和容器集群技术Ku…

RabbitMQ 镜像集群之同步策略_专栏讲解

文章目录一、 基础知识汲取1.1. 镜像集群简述1.2. 策略参数说明1.3. 策略案例二、HA mode 同步方式2.1. 参数说明2.2. 案例2.3. 命令终端形式2.4. MQ管控台设置2.5. 队列同步2.5. 优缺点三、Ha async mode 同步方式&#xff08;推荐使用&#xff09;3.1. 参数说明3.2. 案例3.3.…

bug要改一天?

大家应该都听过下面的一个段子~女生&#xff1a;亲爱的&#xff0c;我来亲戚了男生&#xff1a;多喝热水......女生&#xff1a;我好像要感冒了&#xff0c;亲爱的~男生&#xff1a;多喝热水......女生&#xff1a;我现在好难受哦&#xff0c;快撑不下去了男生&#xff1a;多喝…

新手也能看懂,消息队列其实很简单

本文内容思维导图&#xff1a; 消息队列其实很简单 “RabbitMQ&#xff1f;”“Kafka&#xff1f;”“RocketMQ&#xff1f;”...在日常学习与开发过程中&#xff0c;我们常常听到消息队列这个关键词。我也在我的多篇文章中提到了这个概念。可能你是熟练使用消息队列的老手&am…

java注册是什么意思_springcloud注册中心是什么意思?有什么作用?

可能我们都知道现如今java分布式微服务的流行程度&#xff0c;那么你了解过springcloud的有关内容吗?例如&#xff0c;springcloud中的注册中心是什么?用在何处?注册中心其实就是服务发现的核心。它主要保存了各个可用服务实例的网络地址(指IP Address和Port)。服务注册中心…

如何去设计前端框架能力?星巴克消息开放项目从0到1,从点到面的思考

本文由淘宝前端工程师罗嗣分享&#xff0c;主要讲述了作者在星巴克消息开放项目中的总结和思考&#xff0c;希望对大家有帮助&#xff0c;让业务分享更加有价值。 从满足星巴克项目需求单点出发&#xff0c;发散到从点到面的思考。从而总结了自己思考的基本流程&#xff08;方法…

如何在springboot中使用PageHelper分页插件

文章目录1. pom依赖2. yml3. 实体类4. mapper映射文件4. mapper接口5. service接口6. 实现类7. controller8. 浏览器验证使用思路&#xff1a; 1.引入myabtis和pagehelper依赖 2.yml中配置mybatis扫描和实体类 3. 这2行代码 pageNum&#xff1a;当前第几页 pageNum&#xff1a;…