博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程学习——对象的组合
阅读量:4046 次
发布时间:2019-05-24

本文共 4082 字,大约阅读时间需要 13 分钟。

对象的组合

主要介绍一些组合模式,使得一个类更容易成为线程安全的类,在维护这些类时不会无意中破坏类的安全性保证。

1.设计线程安全的类

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

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

对象的状态

  • 如果所有的域都是基本类型,则这些域构成对象的全部状态;
  • 如果包含其他对象,该对象的状态将包括被引用对象的域。

同步策略规定了如何将不变性条件、线程封闭和加锁机制结合起来以维护线程的安全性,并且规定了哪些变量由哪些锁来保护。

1.1 收集同步需求

**尽量多的使用final域。**final类型的域使用的越多,状态空间就越小,越能简化对象可能状态的分析过程。

单个或多个域上,某一个操作如果存在的无效的状态转换,需要对该操作进行同步。无效的状态转换包括不满足:

  • 不变性条件,例如:int变量超出最大最小值。
  • 后验条件,例如:counter当前值是17,那么下一个操作结束一定是18.

如果某个操作存在无效的状态转换,那么该操作必须是原子的,即需要同步。

1.2 分析依赖状态的操作

某些方法包含一些先验条件才能执行,例如:不能够从空队列中删除一个值。单线程程序中如果遇到无法满足先验条件的情况可以直接返回失败,但是并发程序中先验条件可能因为其他线程的执行而变成真,因此要一直等待先验条件为真再执行。

1.3 分析状态的所有权

所有权在Java中只是一个设计中的要素,在语言层面没有明显的变现。所有权意味着控制权,如果发布了某个可变对象的引用,则意味着共享控制权。在定义哪些变量构成对象的状态时,只考虑对象拥有的数据。

2.同步的实现

同步的实现手段主要有三个:

* 对象封闭
* 委托线程安全性给底层状态变量
* 现有类的封装

2.1 对象封闭

当一个对象封闭在另一个对象中时,由于被访问的对象所有代码路径都是已知的,因此更易于对代码进行分析

被封闭的作用域可以是:

  • 一个实例中:作为一个私有成员
  • 某个作用域中:作为局部变量
  • 线程里:将对象从一个方法传递到另一个方法
通过封闭机制确保线程安全
@GuardedBy("this") private final Set
mySet = new HashSet
();public synchronized void addPerson(Person p) { mySet.add(p);}public synchronized boolean containsPerson(Person p) { return mySet.contains(p);}

需要注意的是,在这个实现中,需要Person也是一个线程安全的类,否则一样会线程不安全。也需要注意,不应该让一个本该封闭的对象逸出作用域。

Java中很多容器比如ArrayList和HashMap不是线程安全的类,但类库通过Collections.synchronizedList及类似方法产生一个容器的“装饰器”,使其访问变成了线程安全的。

2.1.1 Java监视器模式

遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由自己的内置锁保护。

使用监视器模式
public final class Counter {    @GuardedBy("this") private long value = 0;    public synchronized long getValue() {        return value;    }    public synchronized long increment() {        if (value == Long.MAX_VALUE)            throw new IllegalStateException("counter overflow");        return ++value;    }}

Counter中封装了一个变量value,对该变量的所有访问都需要通过Counter的方法执行,这些方法都是同步的。

Java监视器仅仅是一种编码约定。

私有私有锁保护状态
public class PrivateLock {
private final Object myLock = new Object(); @GuardedBy("myLock") Widget widget; void someMethod() { synchronized (myLock) { // Access or modify the state of widget } }}

实现过程中,使用私有锁而不是对象的内置锁可以避免让客户代码错误的得到锁。

2.2 线程安全性委托

如果一个类是由多个独立线程安全的状态变量组成,所有操作中都不会破坏已有的限制条件,那么可以将线程安全性委托给底层的状态变量去做。如果某个类含有复合操作,即多个状态变量之间相互影响,仅仅委托给底层的状态变量去做就不足以保证线程安全性,需要在该类中加锁以保证线程安全性。

发布底层的状态变量 如果一个状态变量是线程安全的,并且没有任何不变性条件约束他的值,也不存在任何不允许的状态转换,那么就可以安全的发布这个变量。例如Counter,value需要是正值(约束条件)且必须递增(如果发布客户可以减少,存在不允许的状态转换),因此不能安全的发布Counter中的value状态。

public class VisualComponent {
private final List
keyListeners = new CopyOnWriteArrayList
(); private final List
mouseListeners = new CopyOnWriteArrayList
(); public void addKeyListener(KeyListener listener) { keyListeners.add(listener); } public void addMouseListener(MouseListener listener) { mouseListeners.add(listener); } public void removeKeyListener(KeyListener listener) { keyListeners.remove(listener); } public void removeMouseListener(MouseListener listener) { mouseListeners.remove(listener); }}

将VisualComponent里面的keyListeners和mouseListeners发不出去是安全的。因为没有在其监听器列表的合法状态上施加任何约束,这些域发布不会破坏线程安全。

2.3 给现有的类中增加功能

给现有的类增加新的功能并保持线程安全性有两种方式。

  • 客户端加锁
    • 将加锁代码分布在多个类中,破坏同步策略的封装性。
  • 组合

以给一个list增加putIfAbsent功能为例:

客户端加锁
class GoodListHelper 
{
public List
list = Collections.synchronizedList(new ArrayList
()); public boolean putIfAbsent(E x) { synchronized (list) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } }}
组合方式
public class ImprovedList
implements List
{
private final List
list; /** * PRE: list argument is thread-safe. */ public ImprovedList(List
list) { this.list = list; } public synchronized boolean putIfAbsent(T x) { boolean contains = list.contains(x); if (contains) list.add(x); return !contains; } // Plain vanilla delegation for List methods. // Mutative methods must be synchronized to ensure atomicity of putIfAbsent. ...}

转载地址:http://eywci.baihongyu.com/

你可能感兴趣的文章
coursesa课程 Python 3 programming course_2_assessment_7 多参数函数练习题
查看>>
coursesa课程 Python 3 programming course_2_assessment_8 sorted练习题
查看>>
在unity中建立最小的shader(Minimal Shader)
查看>>
1.3 Debugging of Shaders (调试着色器)
查看>>
关于phpcms中模块_tag.class.php中的pc_tag()方法的含义
查看>>
vsftp 配置具有匿名登录也有系统用户登录,系统用户有管理权限,匿名只有下载权限。
查看>>
linux安装usb wifi接收器
查看>>
多线程使用随机函数需要注意的一点
查看>>
getpeername,getsockname
查看>>
让我做你的下一行Code
查看>>
浅析:setsockopt()改善程序的健壮性
查看>>
关于对象赋值及返回临时对象过程中的构造与析构
查看>>
VS 2005 CRT函数的安全性增强版本
查看>>
SQL 多表联合查询
查看>>
Visual Studio 2010:C++0x新特性
查看>>
drwtsn32.exe和adplus.vbs进行dump文件抓取
查看>>
cppcheck c++静态代码检查
查看>>
在C++中使用Lua
查看>>
一些socket的编程经验
查看>>
socket编程中select的使用
查看>>