java并发编程实践(2)线程安全性
【0】README
t1)对象的状态:是指存储在状态变量中的数据;t2)共享:意味着变量可以有多个线程同时访问;t3)可变:意味着变量的值在生命周期内可以发送变化;
way1)不在线程之间共享该状态变量;way2)将状态变量修改为不可变的变量;way3)在访问状态变量时使用同步;
public class UnsafeCountingFactorizer extends GenericServlet implements Servlet {private long count = 0;public long getCount() {return count;}public void service(ServletRequest req, ServletResponse resp) {BigInteger i = extractFromRequest(req);BigInteger[] factors = factor(i);++count; // highlight line.encodeIntoResponse(resp, factors);}
}
A1)它包含三个独立的操作: 读取count的值;将值加1;然后将计算结果写入count;(干货——这是一个读取——修改——写入的操作序列,并且其结果依赖于以前的状态)A2)图1.1给出了两个线程在没有同步的case下同时对一个计数器执行递增操作时发生的情况,这不是线程安全的;
<pre name="code" class="java">public class LazyInitRace {private ExpensiveObject instance = null;public ExpensiveObject getInstance() {if (instance == null)instance = new ExpensiveObject();return instance;}
}
class ExpensiveObject { }
A1)在LazyInitRace 中包含了一个竞态条件,它可能会破坏这个类的正确性;A2)假设线程A 和 线程B 同时执行getInstace方法:A看到instance为空, 因此创建一个新的ExpensiveObject 实例;B同样需要判断instance是否为空。此时的instance是否为空,要取决于不可预测的时序,包括线程的调度方式,以及A需要花多长时间来初始化ExpensiveObject 并设置instance;如果B检查到 instance为空, 那么在两次调用getInstance方法时可能会得到不同的结果,即使getInstance通常被认为是返回相同的实例;
public class CountingFactorizer extends GenericServlet implements Servlet { // code2.2.3private final AtomicLong count = new AtomicLong(0); //highlight line. safe thread class.public long getCount() { return count.get(); }public void service(ServletRequest req, ServletResponse resp) {BigInteger i = extractFromRequest(req);BigInteger[] factors = factor(i);count.incrementAndGet(); // highlight line.encodeIntoResponse(resp, factors);}
}
A1)在 java.util.concurrent.atomic包中包含了一些原子变量类,用于实现在数值和对象引用上的原子状态转换;A2)通过用AtomicLong 来代替long类型的计数器,能够确保所有对计数器状态的访问操作都是原子性的;A3)由于servlet的状态就是计数器的状态,并且计数器是线程安全的,因此这里的servlet也是线程安全的;
public class UnsafeCachingFactorizer extends GenericServlet implements Servlet {private final AtomicReference<BigInteger> lastNumber= new AtomicReference<BigInteger>(); //被分解的数值private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>(); //分解后的因子public void service(ServletRequest req, ServletResponse resp) {BigInteger i = extractFromRequest(req);if (i.equals(lastNumber.get()))encodeIntoResponse(resp, lastFactors.get());else {BigInteger[] factors = factor(i);lastNumber.set(i); //highlight line.lastFactors.set(factors); //highlight line.encodeIntoResponse(resp, factors);}}
}
A1)在线程安全性的定义中要求,多个线程之间的操作无论采用何种执行时序或交替方式,都要保证不变性条件不被破坏;A2)UnsafeCachingFactorizer 的不变性条件之一是:在 lastFactors 中缓存的因数之积应该等于在 lastNumber 中缓存的数值;所以当更新某个变量时,需要在同一个原子操作中对其他变量同时进行更新;如第1次请求分解12, 而第2次请求分解20,第3次请求分解20;当请求分解20的时候,lastNumber变了,这就会引起lastFactors 改变;A3)在使用原子引用(AtomicReference)的case下,尽管对set方法的每次调用都是原子的,但仍然无法同时更新lastNumber 和 lastFactors。如果只修改了其中一个变量,那么在这两次修改操作之间,其他线程将发现不变性条件被破坏了;A4)而且,我们也不能保证会同时获取两个值:在线程A获取这两个值的过程中,线程B 可能修改了它们,这样线程A 也会发现不变性条件被破坏了;
Attention) 要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量;(干货——要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量)
synchronized(lock) {
// 访问或修改由锁保护的共享状态
}
public class SynchronizedFactorizer extends GenericServlet implements Servlet {@GuardedBy("this") private BigInteger lastNumber;@GuardedBy("this") private BigInteger[] lastFactors;public synchronized void service(ServletRequest req,ServletResponse resp) {BigInteger i = extractFromRequest(req);if (i.equals(lastNumber))encodeIntoResponse(resp, lastFactors);else {BigInteger[] factors = factor(i);lastNumber = i;lastFactors = factors;encodeIntoResponse(resp, factors);}}
A1)用关键字synchronized来修饰方法service()方法,因此在同一时刻只有一个线程可以执行service方法,这种方法过于极端了,因为多个clients 无法同时使用因式分解,服务的响应性能降低;A2)所以在 synchronized关键字修改service()方法之后,这就变成一个性能问题,而不是线程安全问题了;(干货——非线程安全转为线程安全但却带来了性能问题)
public class Widget {public synchronized void doSth(){...}
}
public class LoggineWidget extends Widget {public synchronized void doSth() {super.doSth();}
}
【4】用锁来保护状态
public class CachedFactorizer extends GenericServlet implements Servlet {@GuardedBy("this") private BigInteger lastNumber;@GuardedBy("this") private BigInteger[] lastFactors;@GuardedBy("this") private long hits;@GuardedBy("this") private long cacheHits;public synchronized long getHits() {return hits;}public synchronized double getCacheHitRatio() {return (double) cacheHits / (double) hits;}public void service(ServletRequest req, ServletResponse resp) {BigInteger i = extractFromRequest(req);BigInteger[] factors = null;synchronized (this) {++hits;if (i.equals(lastNumber)) {++cacheHits;factors = lastFactors.clone();}}if (factors == null) {factors = factor(i);synchronized (this) {lastNumber = i;lastFactors = factors.clone();}}encodeIntoResponse(resp, factors);}
A1)通常,在简单性与性能之间存在着相互制约因素。当实现某个同步策略时,一定不要盲目地为了性能而牺牲简单性(这可能会破坏安全性)A2)当执行时间较长的计算或可能无法快速完成的操作时(例如,网络IO或控制台 IO),一定不要持有锁;
java并发编程实践(2)线程安全性相关推荐
- java并发编程实践(1)intro
[0]README 0.1)本文部分文字描述转自"java并发编程实践",旨在学习"java并发编程实践(1)intro"的相关知识: [3]线程带来的风险 [ ...
- JAVA并发编程实践笔记
2019独角兽企业重金招聘Python工程师标准>>> JAVA并发编程实践笔记 博客分类: java JAVA并发编程实践笔记 1, 保证线程安全的三种方法: a, 不要跨 ...
- 《Java并发编程实践》学习笔记之一:基础知识
<Java并发编程实践>学习笔记之一:基础知识 1.程序与进程 1.1 程序与进程的概念 (1)程序:一组有序的静态指令,是一种静态概念: (2)进程:是一种活动,它是由一个动作序列组成 ...
- Java并发编程实战_一线大厂架构师整理:java并发编程实践教程
并发编程是Java语言的重要特性之一, 在Java平台上提供了许多基本的并发功能来辅助开发多线程应用程序.然而,这些相对底层的并发功能与上层应用程序的并发语义之间并不存在一种简单而直观的映射关系.因此 ...
- java并发编程实践_Java并发编程实践如何正确使用Unsafe
一.前言 Java 并发编程实践中的话: 编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各 ...
- [Java 并发] Java并发编程实践 思维导图 - 第一章 简单介绍
阅读<Java并发编程实践>一书后整理的思维导图.
- java并发编程实践-带完整书签pdf电子扫描版
2007年6月由电子工业出版社出版发行,是一本经典的Java并发参考手册.java并发编程实践随着多核处理器的普及,使用并发成为构建高性能应用程序的关键.Java5以及6在开发并发程序中取得了显著的进 ...
- 《Java并发编程实践-第一部分》-读书笔记
大家好,我是烤鸭: <Java并发编程实战-第一部分>-读书笔记. 第一章:介绍 1.1 并发历史: 多个程序在各自的进程中执行,由系统分配资源,如:内存.文件句柄.安全证书.进程间通信方 ...
- java并发编程第一课 线程的创建、停止和状态变更
开篇词: 由点及面,搭建你的 Java 并发知识网 你好,欢迎学习<Java 并发编程核心 78 讲>,我是讲师星星,一线互联网公司资深研发工程师,参与过集团内多个重点项目的设计与开发. ...
最新文章
- POJ 2135	Farm Tour 最小费用流
- iOS_根据文字字数动态确定Label宽高
- 利用系统滴答时间计算实际程序运行时间
- C++bidirectional dijkstra双向最短路径算法(附完整源码)
- windows下Meteor+AngularJS开发的坑
- 1.IntelliJ IDEA搭建SpringBoot的小Demo
- 为什么软件开发周期总是预估的2-3倍?
- win10系统可禁用的服务器,Win10系统SysMain服务是什么?Win10系统SysMain能不能禁用?...
- linux下tomcat8安装详解详解
- Cocos2d-x学习笔记(十一)动作
- 设计模式入门-模板模式
- 计算机组织原理答案白中英,计算机组成原理答案-白中英
- 一卡通(M1卡)破解过程记录——获取扇区密钥
- 如何用计算机抽奖,怎么用PPT制作随机点名抽奖
- 黑苹果hidp显示不清楚_bigsur 黑苹果开启HIDPI失败解决方法
- 计算机网络管理员中级试题及解析,计算机网络管理员中级考试题及答案(一)(1)...
- matlab单服务台排队系统仿真,matlab单服务台排队系统实验报告.doc
- arcgis10.3添加工具
- Windows10安装报错 由于存在受损的安装文件
- 7种炫酷HTML5 SVG液态水滴融合分解动画特效
热门文章
- cf375D. Tree and Queries
- CF 1529B. Sifid and Strange Subsequences
- 牛客题霸 [没有重复项数字的所有排列] C++题解/答案
- [CQOI2018] 交错序列(矩阵加速优化dp)
- YBTOJ洛谷P4869:出现位置(线性基)
- POJ3678-Katu Puzzle【2-SAT】
- P4343-[SHOI2015]自动刷题机【二分答案】
- jzoj3347,bzoj3257-[NOI2013模拟]树的难题【树形dp】
- jzoj4669-[NOIP2016提高A组模拟7.19]弄提纲【LCA,KMP,字符串】
- ssl1746-商务旅行【tarjan,LCA】