第一章:简介

程序清单1-1非线程安全的数值序列生成器

import net.jcip.annotations.NotThreadSafe;

@NotThreadSafe
public class UnsafeSequence {
private int value; /*返回一个唯一的数值*/
public int getValue() {
return value++; //三个操作:读取,加一,赋值。 多线程并发操作value可能导致步骤被打乱
}
}

程序清单1-2 线程安全的数值序列生成器

public class Sequence {
private int value; /*返回一个唯一的数值*/
public synchronized int getValue() { //各个线程串行访问
return value++;
}
}

第二章:线程安全性

程序清单2-1 一个无状态的Servlet ,各个线程间没有共享状态

@ThreadSafe
public class StatelessFactorizer implements Servlet{ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromRequest(req);
BigInteger[] factories = factor(i);
encodeIntoResponse(res,factories);
}
}

  

程序清单2-3 延迟初始化中的竞态条件(不要这么做)

@NotThreadSafe
public class LazyInitRace { private ExpensiveObject instance = null; //竞态条件 public ExpensiveObject getInstance() {
if(instance == null)
instance = new ExpensiveObject();
return new ExpensiveObject();
}
}
class ExpensiveObject{}

程序清单2-4  使用AtomicLong类型的变量来统计已处理请求的数量

/*servlet的状态就是计数器count的状态,count是线程安全的,所以servlet是线程安全的*/
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0); //原子变量类,实现在数值和对象引用上的原子状态转换 public long getCount() { return count.get(); } public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromReqest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(res,factors);
}
}

程序清单2-5 该Servlet在没有足够原子性保证的情况下对其最近计算结果进行缓存(不要这么做)

/**
* 因数分解:
* 在数学中,因数分解,又称素因数分解,是把一个正整数写成几个约数的乘积。例如,给出45这个数,它可以分解成3×3×5,根据算术基本定理,这样的分解结果应该是独一无二的
*
*
* 希望提升Servlet性能,将最近的计算结果缓存起来,当2个连续的请求对相同的数值进行因数分解时,可以直接使用上一次的计算结果.
*
* 原子引用本身是线程安全的,但是业务逻辑中存在竞态条件
* lastFactors[0] * lastFactors[1] * ..... = lastNumber; 此条件不被破坏,Servlet才是线程安全的。
*/
@NotThreadSafe
public class UnsafeCachingFactorizer implements Servlet {
//lastNumber、lastFactors本身是线程安全的
private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>(); //上一次请求的 因数
private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>(); //上一次请求的 因数分解结果 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromReqest(req);
if(i.equals(lastNumber)){
encodeIntoResponse(res,lastFactors.get());
} else {
BigInteger[] factors = factor(i);
lastNumber.set(i); //无法保证同时与lastFactors更新,
lastFactors.set(factors); //无法保证同时与lastNumber更新
encodeIntoResponse(res,factors);
}
}
}

程序清单2-6 该Servlet能正确地缓存最新的最近计算结果,但并发性确非常糟糕(不要这么做)

@ThreadSafe
public class SynchronizedFactorizer implements Servlet {
@GuardedBy("this") private BigInteger lastNumber; //上一次请求的 因数
@GuardedBy("this") private BigInteger[] lastFactors; //上一次请求的 因数分解结果 public synchronized void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromReqest(req);
if(i.equals(lastNumber)){
encodeIntoResponse(res,lastFactors);
} else {
BigInteger[] factors = factor(i);
lastNumber = i;
lastFactors = factors;
encodeIntoResponse(res,factors);
}
}
}

程序清单2-7,如果内置所不可重置,那么以下代码将发生死锁

/**
* 一线程请求其它线程持有的锁时,发出请求的线程将会被阻塞。
* 然而,内置锁是可重入的,某线程试图获得自己持有的锁时,这个请求将成功。
* “重入” 意味着获取锁的粒度是“线程”,而不是“调用”
*
* “重入”的实现:计数器。锁一次 +1,释放一次-1。 =0时锁被释放。
* @author guchunchao
*
*/
public class Widget {
public synchronized void widget() {
//TODO ......
}
} class LoggingWidget extends Widget {
@Override
public synchronized void widget() {
// TODO ......
super.widget(); //如果内置锁不可重入,这里将无法获得Widget上的锁,因为这个锁已经被持有,而线程将永远停顿下去,等待一个永远也无法获得的锁。
}
}

复合操作的原子性无法通过单个方法的synchronized保证

//Vector类的每一个方法都是synchronized,仅保证单个方法的原子性;当多个方法组合在一起的复合操作时,不足以保证复合操作的原子性,需要额外加锁
if(!vector.contains(name))
vector.add(name);

程序清单2-8  缓存最近执行因数分解的数值及其计算结果的Servlet

//注意:在单个变量上实现原子操作来说,原子变量(Atomic.....)是很有用的,不要把原子变量和同步代码块同时使用,这会带来混乱,也不会再性能或安全性上带来任何好处。

@ThreadSafe
public class SynchronizedFactorizer {
@GuardedBy("this") private BigInteger lastNumber; //上一次请求的 因数
@GuardedBy("this") private BigInteger[] lastFactors; //上一次请求的 因数分解结果
@GuardedBy("this") private long hits; //请求数量
@GuardedBy("this") private long cacheHist; //缓存命中数量 /**获取访Servlet调用问数量*/
public synchronized long getHits() {return this.hits;} /**获取缓存命中率*/
public synchronized double getHitsRatio() {return (double)cacheHist / (double)hits;} /**更细粒度的同步*/
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromReqest(req); //从页面获取待因数分解的数
BigInteger[] factors = null; synchronized(this) {
++hits; //同步竞态条件:请求数 和 缓存命中数 可能同步更改
if(i.equals(lastNumber)){ //命中缓存
++cacheHist; //同步竞态条件:请求数 和 缓存命中数 可能同步更改
factors = lastFactors.clone();
}
} if(factors == null){ //缓存未命中,与上次请求的数值不一样
factors = factor(i); //执行因数分解
synchronized(this) {
lastNumber = i; //同步缓存竞态条件 lastNumber = lastFactors[0] * lastFactors[1] * ......
lastFactors = factors.clone(); //同步缓存竞态条件,用clone是因为factors是在同步代码块儿外定义的变量
}
encodeIntoResponse(res,factors);
}
}
}

  

《JAVA并发编程实战》示例程序第一、二章的更多相关文章

  1. java并发编程实战:第十二章---并发程序的测试

    并发程序中潜在错误的发生并不具有确定性,而是随机的. 安全性测试:通常会采用测试不变性条件的形式,即判断某个类的行为是否与其规范保持一致 活跃性测试:进展测试和无进展测试两方面,这些都是很难量化的(性 ...

  2. 《Java并发编程实战》第十二章 测试并发程序 读书笔记

    并发测试分为两类:安全性测试(无论错误的行为不会发生)而活性测试(会发生). 安全測试 - 通常採用測试不变性条件的形式,即推断某个类的行为是否与其它规范保持一致. 活跃性測试 - 包含进展測试和无进 ...

  3. java并发编程实战:第十四章----构建自定义的同步工具

    一.状态依赖性管理 对于单线程程序,某个条件为假,那么这个条件将永远无法成真 在并发程序中,基于状态的条件可能会由于其他线程的操作而改变 可阻塞的状态依赖操作的结构 acquire lock on o ...

  4. 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记

    一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...

  5. 《Java并发编程实战》第十六章 Java内存模型 读书笔记

    Java内存模型是保障多线程安全的根基,这里不过认识型的理解总结并未深入研究. 一.什么是内存模型,为什么须要它 Java内存模型(Java Memory Model)并发相关的安全公布,同步策略的规 ...

  6. java并发编程实战:第十六章----Java内存模型

    一.什么是内存模型,为什么要使用它 如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果 编译器把变量保存在本地寄存器而不是内存中 编译器中生成的指令顺序,可以与源代码中的顺 ...

  7. java并发编程实战:第十五章----原子变量与非阻塞机制

    非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃 ...

  8. java并发编程实战笔记---(第四章)对象的组合

    4.1设计线程安全的类 包含三个基本要素: 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 2.简历对象状态的并发访问管理策略 对象的状态: 域 基本类型所有域, 引用类型包括被引用 ...

  9. java并发编程实战笔记---(第三章)对象的共享

    3.1 可见性 synchronized 不仅实现了原子性操作或者确定了临界区,而且确保内存可见性. *****必须在同步中才能保证:当一个线程修改了对象状态之后,另一个线程可以看到发生的状态变化. ...

  10. 《Java并发编程实战》第十四章 构建自己定义的同步工具 读书笔记

    一.状态依赖性的管理 有界缓存实现的基类 @ ThreadSafe public abstract class BaseBoundedBuffer<E> { @GuardeBy( &quo ...

随机推荐

  1. 未能加载文件或程序集“System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项

    楼主创建项目的时候选择的是5.2.4的版本,但是后来改成了5.0.0于是出现了这个错误,解决的方法倒也简单 将View文件夹下 web.config文件中 以下两处 版本改成当前版本就行了

  2. vue(9)—— 组件化开发 - webpack(3)

    前面两个终于把webpack相关配置解析完了.现在终于进入vue的开发了 vue组件化开发预热 前期准备 创建如下项目: app.js: footer.js: main.js: webpack.con ...

  3. 为什么CynosDB叫真正的云原生数据库?

    本文由腾讯云数据库发表 注:本文摘自2018年11月22日腾讯云数据库CynosDB新品发布会的演讲实录.随着互联网信息的发展,大家也对云这个词汇也不是特别陌生了,作为全球首选的云服务厂商之一的腾讯云 ...

  4. docker下编译mangoszero WOW60级服务端(三)

    开始构建WOW服务端通用镜像 第二篇文章中准备工作环节已经从github拉取了mangosd源代码,这里我们就可以直接开始编写dockerfile并进行编译 (1) 进入mangos/wow60/ma ...

  5. golang web实战之二(iris)

    之前写了一篇为:golang web实战之一(beego,mvc postgresql) 听说iris更好: 1.  iris hello world package main import &quo ...

  6. LOJ #6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相

    我可以大喊一声这就是个思博题吗? 首先如果你能快速把握题目的意思后,就会发现题目就是让你求出每个点要成为树的重心至少要嫁接多少边 先说一个显然的结论,重心的答案为\(0\)(废话) 然后我们考虑贪心处 ...

  7. 自定义 js 文件的集成引用

    这里的内容, 提前要知道  import comm from ‘...’  和 import {  comm }  from ‘...’ 的基础知识. 我举个案例, 当你有很多api文件的时候, 比如 ...

  8. 最新:百度春节抢百万游戏--汤圆向前冲--辅助工具v1.0.0.2

    https://www.cnblogs.com/Charltsing/p/ADBJumpTY.html 联系QQ:564955427 本程序为Windows版,不要在手机里面打开. 汤圆向前冲辅助工具 ...

  9. java基础-02数据类型

    基本类型 整数 byte byte 数据类型是8位.有符号的,以二进制补码表示的整数 最小值是 -128(-2^7) 最大值是 127(2^7-1) 默认值是 0 byte 类型用在大型数组中节约空间 ...

  10. rabbitmq 出现 com.rabbitmq.client.ShutdownSignalException: , ..................

    -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1. ...