如果需要在多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。
图中的Futures就好比居民楼一层的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待邮件的居民,右侧的t1、t3、t5就好比邮递员。Futures对象需要维护一个列表,存放不同的response对象,为了区分,要对response对象编码加入id值。
实现:
GuardedObject类
public class GuardedObject {
//标识 Guarded Object
private int id;
public GuardedObject(int id){
this.id=id;
}
public int getId(){
return id;
}
//结果
private Object response;
//获取结果
public Object get(int timeout) throws InterruptedException {
synchronized (this){
long begin = System.currentTimeMillis();
//定义经历的时间
long passedTime = 0;
while(response==null){
long waitTime = timeout-passedTime;
//经历的时间超过了最大等待时间,退出循环
if(waitTime<=0){
break;//跳出循环,不再等待
}
this.wait(waitTime);
passedTime = System.currentTimeMillis()-begin;//获得经历时间
}
}
return response;
}
public void complete(Object response){
synchronized (this){
//给结果成员变量赋值
this.response=response;
this.notifyAll();
}
}
}
解耦类MailBoxes/Futures
class MailBoxes{
private static Map<Integer,GuardedObject> boxes = new Hashtable<Integer,GuardedObject>();//用hashtable可以实现对boxex的访问互斥
private static int id=1;
//产生唯一id值,每次自增即可,多个线程可能都会调用id值,故操作id的方法需要加入synchronized
public static synchronized int generateId(){
return id++;
}
/*下边两个方法不需要加上synchronized,因为boxes的方法实现即同步*/
public static GuardedObject createGuardedObject(){
GuardedObject go = new GuardedObject(generateId());
boxes.put(go.getId(),go);
return go;
}
public static Set<Integer> getIds(){
return boxes.keySet();
}
public static GuardedObject getGuardObject(int id){
return boxes.remove(id);//使用remove,从集合中得到GuardedObject类对象后,即从集合中将其删除掉
}
}
居民类People与邮递员类Postman
class People extends Thread{
@Override
public void run() {
//收信
GuardedObject guardedObject = MailBoxes.createGuardedObject();
try {
System.out.println("收信"+guardedObject.getId());
Object mail = guardedObject.get(5000);//收信,不一定等够五秒
System.out.println("收到信内容"+guardedObject.getId()+mail);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Postman extends Thread{
//需要知道送信的信箱id值是什么、信的内容是什么
private int id;
private String mail;
public Postman(int id,String mail){
this.id=id;
this.mail=mail;
}
@Override
public void run() {
GuardedObject guardedObject = MailBoxes.getGuardObject(id);
System.out.println("送信"+id+mail);
guardedObject.complete(mail);
}
}
注意这里两个类均继承Thread类,代表类的每个实例都是一个线程对象
测试代码如下:
class Test20{
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<3;i++){
new People().start();
}
Thread.sleep(1000);
for (Integer id:MailBoxes.getIds()){
new Postman(id,"内容"+id).start();
}
}
}
这里每个Person类和Postman类的对象即为一个线程,相当于先创建三个People线程等待收件,隔1秒后,再创建三个Postman线程去送件。
本例注意点:
还是需要注意多线程访问共享变量的问题,所以如果方法内涉及对共享变量的访问,需要在方法上加synchronized关键字,例子中用到的HashTable类内部的方法实现已经加了synchronized,属于不可变类,所以不需要显式地加上synchronized。