如果需要在多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。
图中的Futures就好比居民楼一层的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待邮件的居民,右侧的t1、t3、t5就好比邮递员。Futures对象需要维护一个列表,存放不同的response对象,为了区分,要对response对象编码加入id值。
实现:
GuardedObject类
public class GuardedObject {//标识 Guarded Objectprivate 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的方法需要加入synchronizedpublic 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{@Overridepublic 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;}@Overridepublic 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。
