java对象组合_java并发编程(三): 对象的组合
對象的組合:
如何將現有的線程安全組件,組合成我們想要的更大規模的程序。
設計線程安全的類:
設計線程安全類的三個要素:
1.找出構成對象狀態的所有變量;
2.找出約束狀態變量的不變性條件;
3.建立對象狀態的并發訪問管理策略。
收集同步需求:
如果不了解對象的不變性條件與后驗條件,那就不能確保線程安全性。要滿足在狀態變量的有效值或狀態轉換上的各種約束條件,就需要借助原子性和封裝性。
依賴狀態的操作:
如果在某個操作中包含有基于狀態的先驗條件,那么這個操作就稱為依賴狀態的操作。如在操作前,判斷當前狀態是否可以進行當前操作。
狀態的所有權:
所有權與封裝性總是相互關聯的:對象封裝它擁有的狀態,即對它封裝的狀態擁有所有權,當然所有權可以通過傳遞對象,變成共享所有權。
實例封閉:
將數據封裝在對象內部,可以將數據的訪問限制在對象的方法上,從而更容易確保線程在訪問數據時總能持有正確的鎖。
/**
* 這里將mySet實例封閉在PersonSet中,
* 盡管HashSet是非線程安全類,
* 由于mySet是私有且不會逸出的,
* 我們通過公共接口提供給外部訪問,但加上了PersonSet內置鎖保護synchronized,
* 因而PersonSet是一個線程安全的類
*/
@ThreadSafe
public class PersonSet {
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);
}
}
封閉機制更易于構造線程安全的類,因為當封閉類的狀態時,在分析類的線程安全性時就無須檢查整個程序。
Java監視器模式:
Java監視器模式的對象會把對象的所有可變狀態都封裝起來,并由對象自己的內置鎖來保護。如Vector, Hashtable等類;
我們也可以通過私有鎖來代替內置鎖:
public class PrivateLock {
private final Object lock = new Object();
public void methodOne(){
synchronized(lock){
// do sth.
}
}
}
線程安全性的委托:
多個線程安全的類組合成的類,不一定就是線程安全的。
/**
* 委托的PersonSet
* 將內部操作委托給線程安全的類SynchronizedSet
* 從而自身也是線程安全的
*/
@ThreadSafe
public class DelegatingPersonSet {
private final Set mySet =
Collections.synchronizedSet(new HashSet());
public void addPerson(Person p){
mySet.add(p);
}
public boolean containsPerson(Person p){
return mySet.contains(p);
}
}
獨立的狀態變量:
我們還可以將線程安全性委托給多個狀態變量,只要這些狀態變量彼此獨立(不相關):
/**
* 將線程安全性委托給多個彼此獨立的狀態變量
* VisualComponent使用CopyOnWriteArrayList(線程安全)來保存監聽器列表
* keyListeners, mouseListeners彼此獨立
* 因此VisualComponent線程安全
*/
public class VisualComponent {
private final List keyListeners =
new CopyOnWriteArrayList<>();
private final List mouseListeners =
new CopyOnWriteArrayList<>();
public void addKeyListener(KeyListener keyListener){
keyListeners.add(keyListener);
}
public void removeKeyListener(KeyListener keyListener){
keyListeners.remove(keyListener);
}
public void addMouseListener(MouseListener mouseListener){
mouseListeners.add(mouseListener);
}
public void removeMouseListener(MouseListener mouseListener){
mouseListeners.remove(mouseListener);
}
}
當委托失效時:
當類內部多個狀態變量,他們之間存在不變性條件,即使這些狀態變量各自是線程安全的,那么該類不一定就線程安全:
/**
* NumberRange不足以保護它的不變性條件
* 并發環境下不安全
*/
@NotThreadSafe
public class NumberRange {
//不變性條件: lower <= upper
private final AtomicInteger lower = new AtomicInteger();
private final AtomicInteger upper = new AtomicInteger();
public void setLower(int i){
if (i > upper.get()){ //不安全的檢查
throw new IllegalArgumentException("lower can't > upper");
}
lower.set(i);
}
public void setUpper(int i){
if (i < lower.get()){ //不安全的檢查
throw new IllegalArgumentException("lower can't > upper");
}
upper.set(i);
}
}
如果一個類是由多個獨立且線程安全的狀態變量組成,并且在所有的操作中都不包含無效狀態轉換,那么可以將線程安全性委托給底層的狀態變量。
發布底層的狀態變量:
如果一個狀態變量是線程安全的,并且沒有任何不變性條件來約束它的值,在變量的操作上也不存在任何不允許的狀態轉換,那么就可以安全地發布這個變量,例如發布上面VisualComponent的keyListeners, mouseListeners。
在現有的線程安全類中添加功能:
通過擴展類,來添加功能。
/**
* 通過擴展實現非重復Vector
*/
public class NoRepeatVector extends Vector {
public synchronized boolean putIfAbsent(E e){
boolean exist = contains(e);
if (!exist)
add(e);
return exist;
}
}
客戶端加鎖機制:
客戶端加鎖:對于使用某個對象X的客戶端代碼,使用X本身用于保護其狀態的鎖來保護這段客戶端代碼。
/**
* 這段客戶端代碼看似線程安全,
* 但其實并不安全,因為鎖住的對象不正確,
* 這里僅是鎖住ListHelper對象,但list對象并沒有被鎖住,
* 其他客戶端仍可在不安全情況下對list進行操作
*/
@NotThreadSafe
public class ListHelper {
public List list =
Collections.synchronizedList(new ArrayList());
public synchronized boolean putIfAbsent(E x){
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
} 所以上面的代碼,我們應該對list加鎖,而不是ListHelper對象:
@ThreadSafe
public class SafeListHelper {
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;
}
}
}
組合:
當為現有的類添加一個原子操作時,有一種更好的方法:組合(Composition)。
/**
* 通過組合實現"若沒有則添加" 下午4:48:42
*/
@ThreadSafe
public class improvedList implements List {
private final List list;
public improvedList(List list) {
this.list = list;
}
public synchronized boolean putIfAbsent(T t){
boolean absent = !list.contains(t);
if (absent)
list.add(t);
return absent;
}
@Override
public synchronized int size() {
return list.size();
}
...
}
將同步策略文檔化:
在文檔中說明客戶代碼需要了解的線程安全性保證,以及代碼維護人員需要了解的同步策略。
不吝指正。
總結
以上是生活随笔為你收集整理的java对象组合_java并发编程(三): 对象的组合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-基本运算符
- 下一篇: python中__init__文件的运用