第4章主要介绍如何构造线程安全类。

在设计线程安全类的过程中,需要包含以下三个基本要素:

  • 找出构成对象状态的所有变量。
  • 找出约束状态变量的不变性条件。
  • 建立对象状态的并发访问管理策略。

构造线程安全类常采用的技术如下:

  • 实例封闭

当一个对象被封装到另一个对象中时,能够访问被封装对象的所有代码路径都是已知的。与对象可以由整个程序访问的情况相比,更易于对代码进行分析。通过将封闭机  制与合适的加锁策略结合起来,可以确保以线程安全的方式来使用非线程安全的对象。

对象一般可以封闭在三种地方:

  1. 封闭在类的一个实例中,例如作为类的一个私有成员。
  2. 封闭在某个作用域内,例如作为一个局部变量。
  3. 封闭在线程内,例如在某个线程中将对象从一个方法传递到另一个方法,而不是在多个线程之间共享该对象。

在Java平台的类库中有很多线程封闭的示例,比如一些基本的容器类并非线程安全的,例如ArrayList和HashMap,但类库提供了包装器工厂方法,使得这些非线程安全的类可以在多线程环境中安全地使用。

  • 委托

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

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

例子:假设需要一个线程安全的链表,它需要提供一个原子的“若没有则添加(put-if-Absent)”的操作

1.扩展

 public class BetterVector<E> extends Vector<E> {
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent) {
add(x);
}
return absent;
}
}

这种方法的缺点是:导致了现在的同步策略实现被分布到多个单独维护的源代码文件中,一旦底层的类改变了同步策略并选择了不同的锁来保护它的状态变量,那么子类会被破坏,因为在同步策略改变后无法再使用正确的锁来控制对基类状态的并发访问。

2.客户端加锁机制

 public class ListHelper<E> {

     public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
} }

这种方式很脆弱,因为它将类C的加锁代码放到与C完全无关的其他类中,会导致混乱的。

客户端加锁机制与扩展类机制的共同点:将派生类的行为与基类的实现耦合在一起,扩展破坏了实现的封装性,客户端加锁破坏了同步策略的封装性。

3.组合

 public class ImprovedList<E> implements List<E> {

     private final List<E> list;

     public ImprovedList(List<E> list) {
this.list = list;
} public synchronized boolean putIfAbsent(E x) {
boolean contains = list.contains(x);
if (contains) {
list.add(x);
}
return !contains;
} public synchronized void clear() {
list.clear();
}
//....按照类似的方式委托List的其他方法
}

这里其实使用了Java监视器模式来封装现有的List,并且只要在类中拥有指向底层List的唯一外部引用,就能确保线程安全性。

最后需要将同步策略文档化。

Java并发编程实战4章的更多相关文章

  1. JAVA并发编程实战---第二章:线程安全性

    对象的状态是指存储在状态变量中的数据.对象的状态可能包括其他依赖对象的域.例如HashMap的状态不仅存储在HashMap本身,还存储在许多Map.Entry对象中.对象的状态中包含了任何可能影响其外 ...

  2. Java并发编程实战3章

    1.同步包括两方面:原子性和可见性. 2.可见性:因为在多线程程序中,如果没有采用正确的同步,有些线程就会得到失效数据. Java内存模型要求,变量的读取操作和写入操作都必须是原子操作,但对于非vol ...

  3. Java并发编程实战---第六章:任务执行

    废话开篇 今天开始学习Java并发编程实战,很多大牛都推荐,所以为了能在并发编程的道路上留下点书本上的知识,所以也就有了这篇博文.今天主要学习的是任务执行章节,主要讲了任务执行定义.Executor. ...

  4. 《Java并发编程实战》/童云兰译【PDF】下载

    <Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...

  5. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

  6. Java并发编程实战 03互斥锁 解决原子性问题

    文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...

  7. Java并发编程实战 04死锁了怎么办?

    Java并发编程文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 前提 在第三篇 ...

  8. Java并发编程实战 05等待-通知机制和活跃性问题

    Java并发编程系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 Java并发编程实 ...

  9. Java并发编程实战——读后感

    未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...

随机推荐

  1. 003杰信-在jsp页面输入数据,然后在oracle数据库中插入factory数据,当字段允许为空时要特殊处理

    本博客的内容全部来自于传智播客,特在此说明. 业务要求如下:在jsp页面(jFactoryCreate.jsp)上输入数据时,转到后台,并输入到数据库. jFactoryCreate.jsp页面:

  2. 【BZOJ】1668: [Usaco2006 Oct]Cow Pie Treasures 馅饼里的财富(dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1668 裸dp.. f[i][j]表示i行j列最大能拿到 f[i][j]=max(f[i+1][j-1 ...

  3. 工作中php处理HTTP请求的缺陷总结

    之前遇到过php在处理一部分业务的时候总是感觉有短板,有些东西总是不能随人心意.比如执行时间问题,一个进程需要读取多条数据放入数组循环执行处理流程,就会很慢,容易超时,这个超时问题 可以用php设置s ...

  4. Hadoop1.2.1 出现Warning: $HADOOP_HOME is deprecated.的解决方案

    通过启动或停止hadoop我们会发现会出现 “Warning: $HADOOP_HOME is deprecated” 这样一个警告,下面给出解决方案: 不过我们一般推荐第二种,因为我们还是需要$HA ...

  5. recyclerView中的方法

    onCreateViewHolder(); onBindViewHolder(); getItemCount(); recyclerVIew中没有添加头布局和尾布局方法.可以用getItemViewT ...

  6. 关于recycler遇到的问题

    1.//设置recyclerView不能点击myLayoutManager.setScrollEnabled(false);class MyLayoutManager extends LinearLa ...

  7. C语言switch语句

    C语言虽然没有限制 if else 能够处理的分支数量,但当分支过多时,用 if else 处理会不太方便,而且容易出现 if else 配对出错的情况.例如,输入一个整数,输出该整数对应的星期几的英 ...

  8. vue+webpack2实现路由的懒加载

    当打包构建应用时,Javascript 包会变得非常大,影响页面加载.如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了. 结合 Vue 的异步组 ...

  9. Codeforces Round #359 (Div. 2) C. Robbers' watch 搜索

    题目链接:http://codeforces.com/contest/686/problem/C题目大意:给你两个十进制的数n和m,选一个范围在[0,n)的整数a,选一个范围在[0,m)的整数b,要求 ...

  10. 在 Ubuntu Mate 16.04 上通过 PPA 升级 Mate 1.14

    导读 Mate 桌面环境 1.14 现在可以在 Ubuntu Mate 16.04 ("Xenial Xerus") 上使用了.根据这个版本的描述,为了全面测试 Mate 1.14 ...