JAVA并发编程学习:构造线程安全的对象

原创
2016/08/29 10:02
阅读数 290

设计线程安全的类

实例限制

  当一个对象被另一个对象封装时,所有访问被被封装对象的代码路径就是全部可知的,这相比于让对象可被整个系统访问来说,更容易对代码路径进行分析。将数据封装在对象内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁

  被限制对象一定不能溢出到它的期望可用的范围之外。可以把对象限制在本地变量、类的实例(比如私有类的成员)或线程(比如对象在线程内部从一个方法传递到另一个方法,不过前提是该对象不被跨线程共享),下面的例子未对Person的线程安全性作任何假设,但如果它是可变的,那么可能还需要额外同步 

@ThreadSage
public class PersonSet
{
   @GuardedBy("this")
   private final Set<Person> mySet=new HashSet<Person>();
   
    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<String, MutablePoint> locations;
	
	public MonitorVehicleTracker(Map<String, MutablePoint> locations)
	{
		this.locations=deepCopy(locations);
	}
	
	public synchronized Map<String, MutablePoint> 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<String,MutablePoint> deepCopy(Map<String, MutablePoint> m)
	{
		Map<String,MutablePoint> result=new HashMap<String,MutablePoint>();
		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

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部