励精图治---Concurrency---组合对象
来源:互联网 发布:淘宝清洗 编辑:程序博客网 时间:2024/05/21 09:01
加一句:委托才是解决线程安全最有效的策略。
用现有的线程安全类去管理状态变量。
判断一个类是否是线程安全的?要看三方面。
1. 查看类中所有的状态变量
2. 找出这些状态变量的约束条件,可变的,不可变的
3. 查看,是否对这些状态变量有并发访问管理。这里要注意所加的锁是怎样的。private this 又或者其他。
观点总结
状态变量是越少越简单。需要判断的状态,组合状态也就少了。复杂度肯定下降。
对现有的状态变量,怎么做?
1. 熟悉其特定的属性,比如一定在某个range之内。比如计数器t. 他一定不是负数。
2. 熟悉其后续要验证的状态
3. 这些状态变量是否有牵连?是否会组合使用?要确保其原子性。复合操作也要是具有原子性
4. 要注意一些先决条件,一些判断条件,等待,这伴随着锁的释放和请求,要谨慎。
5. 对象可能会包含 多个对象,多个域,对这些对象的操作,是否是线程安全的。要注意其原子性跟内存可见性。
6. 在C++操作中,我们会考虑析构。这个内存回收问题在java中被稀释。但依旧需要注意,对象在传递的时候,关于这个对象的引用是否被传递,如果publish了被共享的变量或者可变的变量,那么又将涉及访问控制的问题
将状态变量封装
将状态变量封装在对象内部,就可以将数据的访问限制在对象的方法上,这样就更加容易管控。
java中,有很多设计模式,比如观察者模式。工厂模式都可以将数据的访问控制在对象中。
限制了锁的被访问区域,也就杜绝了锁的被意外获得。而这种意外获得可能会造成吞吐量问题,同时,一旦出现问题,也可以将问题锁定在对象内,而不是整个程序。
private class PrivateLock {
private final Object mLock = new Object();//#这个锁是私有的。也就无法被外部类访问
void doSomething() {
synchronized(mLock) {
................
}
}
}
活用现成的线程安全类
当我们从头开始构建一个类,或者多线程不安全类组合成一个类,java的观察者模式就可以解决这个问题。
将线程不安全的类添加封装,封装进线程安全类。
在set get的过程中,我们会出现线程安全问题,但是如果这个过程是通过ConcurrentHashMap之类线程安全的类呢。问题迎刃而解。
多状态变量的组合
public class NumberRanger {
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i ){
if(i > upper.get()) throw IllegalArgumentException ("can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i ){
if(i < lower.get()) throw IllegalArgumentExdeption (can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean is InRange(int i){
return (i >= lower.get() && i <=upper.get());
}
}
setLower跟setUpper单独看具有原子性,但是这两者是有关联的。假定当前值(2, 8), 设值成(6, 5),在正常情况下判断,6<8 ,判断通过, 5大于2 判断通过,于是,如果这个时候多线程执行时序出错,那么,就可以设置成(6, 5),这显然是错误的。
怎么解决呢?
多个独立的状态变量组合使用,要将这个复合操作委托给状态变量。
可以额外添加private final Object myLock =new Object();
public class NumberRanger {
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
private final Object myLock =new Object();
public void setLower(int i ){
synchronized(myLock){
if(i > upper.get()) throw IllegalArgumentException ("can't set lower to " + i + " > upper");
lower.set(i);
}
}
public void setUpper(int i ){
synchronized(myLock){
if(i < lower.get()) throw IllegalArgumentExdeption (can't set upper to " + i + " < lower");
upper.set(i);
}
}
public boolean is InRange(int i){
synchronized(myLock){
return (i >= lower.get() && i <=upper.get());
}
}
}
这里将多个独立的状态变量,委托给了myLock这个状态变量。
线程安全类如何添加新功能?
步骤:
1. 要注意类的锁有几个
2. 这些锁是否跟你要添加的功能有关系
3. 找对你要添加的功能相关的锁
public class ListHelper<E>{
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
........
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) list.add(x);
return absent;
}
}
这段代码是错误的。原因在于,要添加的新功能是 无则添加。list是一个线程安全的类,ListHelper实际上是对list的一种封装。
这里的synchronized是对ListHelper进行的同步,这里的对象是this,即ListHelper。但是实际上list之所以安全是因为在list内部有锁。那么这里锁的对象就错了。
修改如下:
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;
}
}
}
锁的对象改成list。
封装---将线程非安全类整成线程安全类
通过实现一个线程非安全类,将其接口封装进当前类中,而将当前类设计成线程安全的。通过this锁增加一层额外的锁。使得这个线程非安全类变得安全。
文档的重要性
自己写的类,要有详细的文档。别人的类没有说明,就当从设计者的角度去分析去猜。为了避免猜测,要建立足够详细的说明。
- 励精图治---Concurrency---组合对象
- 励精图治---Concurrency---共享对象
- 励精图治---Concurrency---小结
- 励精图治---Concurrency---取消线程
- 励精图治---Concurrency---GUI设计
- 励精图治---Concurrency---怎么测??
- 励精图治---Concurrency---起步线程安全
- 励精图治---Concurrency---如何创建多线程
- 励精图治---Concurrency---ThreadPoolExecutor最详
- 励精图治---Concurrency---为什么要有线程
- 励精图治---Concurrency---线程池啊线程池
- 励精图治---Concurrency---吞吐量、活跃性及死锁
- 励精图治---Concurrency---利用线程安全类来构建
- [Java Concurrency in Practice]第四章 对象的组合
- Java并发编程学习——《Java Concurrency in Practice》学习笔记 4.对象的组合
- Concurrency
- Concurrency
- Concurrency
- java容器类---Vector
- HDU1875-畅通工程再续-最小生成树
- 南邮 OJ 2059 S4全球总决赛(2)
- uva 11729 贪心 + STL 应用
- 程序员保值的五个关键点
- 励精图治---Concurrency---组合对象
- 南邮 OJ 2060 数数问题
- Default DispatcherServlet Configuration
- cocos2dx-触屏事件
- Josephus(约瑟夫环)问题
- app后端设计(11)-- 系统架构(2014.12.05更新)
- IETester和DebugBar的安装与使用
- 南邮 OJ 2061 走迷宫
- 8 个最优秀的 Android Studio 插件