Java并发编程实战————对象的组合

引言

对象的组合,是《Java Concurrency in Practice》中第四章引入的课题。这并不是一个并发的概念。

为了可以将现有的线程安全组件组合为更大规模的组件或程序,而不是每次内存访问都进行分析以确保程序是线程安全的。这一章将介绍一些组合模式,这些模式可以更容易的使一个类成为线程安全的类,并且维护性更强。

一、设计线程安全的类

为了在不对整个程序进行分析的情况下就可以得出一个类是否是线程安全类的结论,总结了设计线程安全类的三个基本要素。

找出构成对象状态的所有变量。

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

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

 对象的状态指的是那些基本类型的变量。如果在对象的域中引用了其他对象,那么该对象的状态将包含被引用对象的域。

1.1 什么是同步策略?

同步策略的意思是保证对象不变性条件后验条件的前提下协同各个访问操作。是不变性条件、线程封闭、加锁机制、锁保护的相关概念的一个统称。

1.2 什么是不变性条件?

不变性条件指的是在对象状态空间内的一种逻辑约束。

状态空间简单的说就是对象的状态所有的可能值。而不变性条件就是人为规定的在状态空间内只能取哪些值。比如:

public class Counter {private long value = 0;public long increment() {if (value == Long.MAX_VALUE)throw new IllegalStateException("计数器溢出");return ++value;}
}

Counter类的对象有一个long类型的value,那么状态空间就是Long.MIN_VALUE 到 Long.MAX_VALUE之间所有的整型,但是由于该类的方法只提供了一个增长的方法,而value的初始值又是0,因此,对于这个类的不变性条件,就是value不能是负数。

1.3 状态迁移

对象的状态通过相关的方法产生了变化,这就是状态迁移。

1.4 后验条件

人为规定的状态迁移后的状态的有效性条件。比如上面的代码中,如果此时value是17,那么执行increment()后value一定要等于18。那么这里的后验条件就是状态改变后的值比状态改变前大1。

另外,当下一个状态需要依赖当前状态时,这个操作就必须是复合操作。但是,并不是所有操作都会在状态迁移上施加限制,例如,温度变量、彩票变量。

1.5 不变形条件与原子性

如果,不变性条件包含多个变量,那么将产生原子性的需求:这些相关的变量必须在单个原子操作中进行读取或更新。简单地说,就是不能先更新一个变量,然后释放锁,再获取锁,再去更新另一个相关变量。因为多个变量构成的不变性条件是整体性的,如果分开更新相关的状态,那么在中间的某个时刻必然会导致对象处于失效状态。

1.6 先验条件

简单地说就是,必须满足某种要求程序才能继续执行的条件。它属于一种依赖的状态。

单线程中的某个操作如果无法满足先验条件,则必然失败;多线程下可能会由于其他线程执行的操作而变为真。

并发程序中一定要等到先验条件为真,然后再执行该操作。这就引出了另一个相关的机制:Java的线程通信机制。比如等待和通知、阻塞等。

1.7 状态的所有权

对象对它封装的状态拥有所有权。所有权意味着控制权。

二、实例封闭(Instance Confinement)

如果某个对象不是线程安全的,有很多手段可以使它在多线程程序中正常使用。可以使用线程封闭技术确保这个对象只能由单个线程访问;或者通过锁来保护对象的所有访问。

其实,实例封闭技术在日常开发中经常使用。简单的说,对象A作为一个私有成员封装在了对象B中,那么对象A就是一个封闭的实例,A的访问也可以得到有效的控制。

例如下面的程序中,PersonSet的状态由HashSet来管理,而HashSet并非线程安全的。但由于mySet是私有的并且不会逸出,因此HashSet被封闭在PersonSet中。唯一能访问mySet的代码路径是addPerson与containsPerson,在执行它们时都要获得PersonSet上的锁。PersonSet的状态完全由它的内置锁保护,因而PersonSet是一个线程安全的类。

public class PersonSet {private final Set<Person> mySet = new HashSet<>();public synchronized void addPerson(Person p) {mySet.add(p);}public synchronized boolean containsPerson(Person p) {return mySet.contains(p);}
}

2.1 监视器模式

监视器模式并不是Java的GoF 23设计模式。什么是监视器模式?将对象的所有可变状态都封装起来,并且只能通过内置锁来访问,这就是监视器模式。而内置锁synchronized也成为监视器或监视器锁。

Java监视器模式只是一种编吗约定:对于任何一种锁对象,只要自始至终都使用该锁对象,都可以用来保护对象的状态。

监视器模式的两个代表:Vector和Hashtable。

2.2 私有锁对象

私有锁对象而不是对象的内置锁,可以将锁封装起来,使客户代码只能通过共有方法来访问锁。

public class PrivateLock {private final Object myLock = new Object();@GuardBy("myLock")Widget widget;void someMethod() {synchronized (myLock) {// 访问或修改Widget的状态}}
}

 

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

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

相关文章

史上最真实行业鄙视链

本文转载自菜鸟教程的微信公众号&#xff0c;原文链接&#xff1a;https://mp.weixin.qq.com/s/d9cdtq8y4Msq-_ZNof-iuw 引言 作为程序员的一份子&#xff0c;掌握好各个生态系统中的鄙视链&#xff0c;可以写出更加符合改变世界要求的代码。掌握了鄙视链&#xff0c;就掌握了…

如何快速理清大型项目业务逻辑

引言 本篇文章为了探讨如何快速上手一个大型项目。针对经验尚浅需要快速接手一个项目的开发人员。 当他们拿到一个大型程序后&#xff0c;他们便开始一句一句的阅读分析&#xff0c;夜以继日&#xff0c;悬梁刺股。可结果依然不理想&#xff0c;往往进入以下状态&#xff1a;…

权限验证框架Shiro

权限验证框架Shiro&#xff1a; Shiro简介 什么是Shiro&#xff1a; shiro是一个强大易用的Java安全框架&#xff0c;提供了认证&#xff0c;授权&#xff0c;加密&#xff0c;回话管理等功能&#xff1b; 认证&#xff08;Authentication&#xff09;&#xff1a;用户身份识别…

Linux——less指令常用操作

引言 对于生产环境、测试环境中的日志文件&#xff0c;我们可以通过less指令来进行查看并通过关键字进行查找。less命令的含义是&#xff1a;分屏查看文件内容。 它要比more命令更加强大&#xff0c;less在显示文件内容时&#xff0c;并不是一次将整个文件加载之后才显示&…

Mybatis从入门到精通上篇

Mybatis从入门到精通上篇&#xff1a; 学习过的持久层框架&#xff1a;DBUtils , Hibernate Mybatis就是类似于hibernate的orm持久层框架。 Mybatis介绍&#xff1a; Mybatis是面向sql的持久层框架&#xff0c;他封装了jdbc访问数据库的过程&#xff0c;我们开发&#xff0c;只…

Eclipse使用————Working Set工作集

引言 经常看到在设置项目的时候&#xff0c;如导入项目&#xff0c;新建项目等看到对话框的下方有一个“add to working set”复选框&#xff0c;为了弄清这个working set&#xff0c;我们就来好好了解一下Eclipse 的working set功能。 Working Set&#xff1f; Eclipse中通…

Mybatis从入门到精通下篇

Mybatis从入门到精通下篇&#xff1a; 输入类型&#xff1a; 输出类型&#xff1a; ResultMap&#xff1a; 动态sql&#xff1a; if标签&#xff1a; where标签&#xff1a; sql片段&#xff1a; foreach标签&#xff1a; 关联查询&#xff1a; 以订单作为主体&#xff1a; 一…

爱上进制转换练习

引言 对于可能接触到通讯行业或是物联网的开发工作者&#xff0c;一般会面对十进制、二进制、十六进制的转换工作&#xff0c;不仅仅是体现在代码上&#xff0c;有时候也需要用肉眼来进行快速的转化&#xff0c;以获取协议指令中的信息。 今天通过简单的整理&#xff0c;特此…

Sprint Boot————@Qualifier、@Primary

引言 使用Autowired自动注入时&#xff1a; 如果注入的接口有多个实现类&#xff0c;如下所示&#xff1a; 那么如果不指定具体是哪个实现类的Bean&#xff0c;在Spring Boot启动时就会发生异常&#xff08;下图请点击查看&#xff09;&#xff1a; 异常的描述信息非常简单&am…

SpringMVC教程上篇

SpringMVC教程上篇 SpringMVC优势&#xff1a; SpringMVC代码执行流程&#xff1a; 框架结构&#xff1a; 架构流程&#xff1a; 组件说明&#xff1a; SpringMVC与Mybatis整合 ! 效果: 开发流程&#xff1a;

Eclipse使用————生成Get/Set、toString快捷键(不使用鼠标)

引言 除了鼠标右键空白处—>source选择我们需要的操作之外是否还有更快捷的不需要鼠标的操作呢&#xff1f; 如何快速的通过键盘来生成get、set方法&#xff1f;如何快速的通过键盘生成toString方法&#xff1f;如何快速的通过键盘生成需要实现的父类方法呢&#xff1f; …

SpringMVC教程下篇

SpringMVC教程下篇 内容包括&#xff1a; 绑定数组&#xff1a; 将表单数据绑定到list&#xff1a; RequestMapping注解的三种用法&#xff1a; Controller方法返回值&#xff1a; 乱码问题总结 异常处理&#xff1a; 照片上传&#xff1a; RESTFUL支持&#xff…

Spring Boot面试杀手锏————自动配置原理

引言 不论在工作中&#xff0c;亦或是求职面试&#xff0c;Spring Boot已经成为我们必知必会的技能项。除了某些老旧的政府项目或金融项目持有观望态度外&#xff0c;如今的各行各业都在飞速的拥抱这个已经不是很新的Spring启动框架。 当然&#xff0c;作为Spring Boot的精髓…

为什么要坚持写博客

引言 断断续续地写博客已经有一段时间了&#xff0c;作为一个Java中级开发工程师&#xff0c;工作了三年多也算渐渐入了门。不得不说&#xff0c;博客给我的改变是非常大的&#xff0c;那么作为一个技术人员&#xff0c;为什么我觉得必须要坚持写博客&#xff1f;下面&#xf…

Spring Boot——@ConfigurationProperties与@Value的区别

引言 Spring Boot从配置文件中取值的方式有两种&#xff0c;一种是批量注入ConfigurationProperties&#xff0c;另一种是单独注入Value。 它们之间除了批量与单独取值的区别之外&#xff0c;还存在着其他一些使用方式&#xff0c;本篇博客将详细讲解这两种注解之间的区别和使…

Spring Boot —— YAML配置文件

引言 首先&#xff0c;YAML并不是仅仅可以使用在Java项目中&#xff0c;它是一种类似于json结构的标记语言&#xff0c;可以为所有的编程语言服务。它强调更直观的层级表示&#xff0c;比较适合描述配置文件中的层级关系。 Spring Boot可以识别后缀名为".properties&quo…

centos7下docker启动失败解决

centos7下docker启动失败解决 docker安装成功却启动失败&#xff0c;查看docker服务&#xff0c;systemctl status docker.service, 服务日志提示Failed to start Docker Application Container Engine.如下图所示&#xff1a; 解决方法&#xff0c;修改docker文件&#xff0…

Java并发编程实战————Semaphore信号量的使用浅析

引言 本篇博客讲解《Java并发编程实战》中的同步工具类&#xff1a;信号量 的使用和理解。 从概念、含义入手&#xff0c;突出重点&#xff0c;配以代码实例及讲解&#xff0c;并以生活中的案例做类比加强记忆。 什么是信号量 Java中的同步工具类信号量即计数信号量&#x…

Effective Java(一)———— 代替构造器和Setter的构建器模式

引言 Java语言中的一部经典著作《Effective Java》&#xff0c;里面涵盖了78条我们应该熟练的Java编程技巧。 本篇博客是该书学习的系列笔记第一篇。本系列博客不会与书中的78条建议完全匹配。只是以一种读者的身份来记录和总结从书中得到的好的编程建议&#xff0c;博客中会…

LeetCode算法入门- Palindrome Number-day2

LeetCode算法入门- Palindrome Number-day2 Palindrome Number Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward. Example 1: Input: 121 Output: true Example 2: Input: -121 Output: false Ex…