格言:在程序猿界混出点名堂!
![](https://img.haomeiwen.com/i18759109/f022812d61509771.png)
《JAVA并发编程实战》解读
【连载】第2章-2.2原子性
回顾:上一节主要介绍什么是线程安全性,这一节讲的是大家经常听到的原子性。
比如Atomic
包、事务的特征ACID
,其中的A指的就是原子性。
原子性
先看一下下面线程不安全的例子:
@NotThreadSafe
public class UnsafeCountingFactorizer 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++;
encodeIntoResponse(resp,factors);
}
}
第1章中我们有提到过i++
,是三步完成的非原子操作,如果这个servlet是多线程同时访问,可能丢失计数。
竞态条件
根据上面的例子,引申这个定义:
竞态条件:由于不恰当的执行时序,而出现不正确的结果。
第1章举过这个例子:A线程获取i=0,B线程获取i=0,A线程修改i=1,B线程修改i=1,A线程写入i=1,B线程写入i=1,导致最终结果出错,这个就是竞态条件。
- 延迟初始化的竞态条件
最常见的竞态条件的类型就是“
先检查后执行
”
延迟初始化导致竞态条件的例子,非线程安全的单例
:
@NotThreadSafe
public class LazyInitRace{
private ExpensiveObject instance = null;
public ExpensiveObject getInstance(){
if(instance == null)
instance = new ExpensiveObject();
return instance;
}
}
上面的例子包含了一个竞态条件
,这里例子存在的问题我就不解释,大家天天在使用的单例设计模式。
- 复合操作
为了避免竞态条件,我们需要将复合操作原子化,即将“先检查后修改”或者“获取-修改-写入”这样的操作封装具有原子性原子性,看一下开篇提到的非线程安全的示例如何进行复核操作原子性:
@ThreadSafe
public class UnsafeCountingFactorizer implements Servlet{
// 线程共享
private AtomicLong count = new AtomicLong(0);
public long getCount(){
return count.get();
}
public void service(ServletRequest req, ServletResponse resp){
BigInteger I = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp,factors);
}
}
AtomicLong的incrementAndGet
这个操作为原子操作,采用volatile+CAS的方式来更新保证共享变量的原子性。
知识点
- 理解
原子性
、竞态条件
、复合操作
。
喜欢连载可关注
简书
或者微信公众号
:
简书专题:Java并发编程实战-可爱猪猪解读
https://www.jianshu.com/c/ac717321a386
微信公众号:逗哥聊IT
。
![]()
网友评论