設計線程安全的類
實例限制
當一個對象被另一個對象封裝時,所有訪問被被封裝對象的代碼路徑就是全部可知的,這相比于讓對象可被整個系統訪問來說,更容易對代碼路徑進行分析。將數據封裝在對象內部,把對數據的訪問限制在對象的方法上,更易確保線程在訪問數據時總能獲得正確的鎖
被限制對象一定不能溢出到它的期望可用的范圍之外。可以把對象限制在本地變量、類的實例(比如私有類的成員)或線程(比如對象在線程內部從一個方法傳遞到另一個方法,不過前提是該對象不被跨線程共享),下面的例子未對Person的線程安全性作任何假設,但如果它是可變的,那么可能還需要額外同步
@ThreadSage
public class PersonSet
{
@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);
}
}
監視器
使用私有鎖對象,而不是對象的內部鎖(或任何其他可公共訪問的鎖)
public class PrivateLock
{
private final Object myLock=new Object();
@GuardedBy("myLock") Widget widget;
void somenMethod()
{
synchronized(myLock)
{
//訪問或修改widget的狀態
}
}
}
范例:機動車追蹤器
package com.henrysun.javaSE.bfbc;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.annotation.GuardedBy;
/**
* 線程安全類,JAVA監視器模式
* 范例:機動車追蹤器
* 一個用于調度出租車,警車,貨運卡車等機動車的“機動車追蹤器”,每一輛機動車都有一個String標識
* 并有一個與之對應的位置(X,Y),視圖線程和多個更新線程會并發的訪問數據模型
* @author Sam Flynn
*
*/
public class MonitorVehicleTracker {
@GuardedBy("this")
private final Map locations;
public MonitorVehicleTracker(Map locations)
{
this.locations=deepCopy(locations);
}
public synchronized Map getLocations() {
return deepCopy(locations);
}
public synchronized MutablePoint getLocation(String id) {
MutablePoint loc=locations.get(id);
return loc==null?null:new MutablePoint(loc);
}
public synchronized void setLocation(String id,int x,int y)
{
MutablePoint loc=locations.get(id);
if(loc==null)
{
throw new IllegalArgumentException("No such ID:"+id);
}
loc.x=x;
loc.y=y;
}
private static Map deepCopy(Map m)
{
Map result=new HashMap();
for(String id:m.keySet())
{
result.put(id, new MutablePoint(m.get(id)));
}
return Collections.unmodifiableMap(result);
}
}
/**
* 可變point
* @author Sam Flynn
*
*/
class MutablePoint
{
public int x,y;
public MutablePoint(){x=0;y=0;}
public MutablePoint(MutablePoint p)
{
this.x=p.x;
this.y=p.y;
}
}
先復制可變的數據,再返回給用戶,這種實現方式只能簡單的維護線程安全。如果locations集合不是非常大的話,這種做法通常不會造成性能問題。每次調用getLocations前先復制全部數據還會產生另一種副作用-即使真實的location已經改變,返回的容器的內容仍然不會改變
委托線程安全
我們向一個無狀態的類(假設A)中添加一個線程安全的類型(假設B)的屬性,所得組合對象仍然是線程安全的,因為A的狀態就是線程安全類B的狀態,并且A沒有對B的狀態增加額外的有效性的約束,可以說A將它的線程安全性委托給了B
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态