如果需要在多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。
image.png
图中的Futures就好比居民楼一层的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待邮件的居民,右侧的t1、t3、t5就好比邮递员。Futures对象需要维护一个列表,存放不同的response对象,为了区分,要对response对象编码加入id值。

实现:

GuardedObject类

  1. public class GuardedObject {
  2. //标识 Guarded Object
  3. private int id;
  4. public GuardedObject(int id){
  5. this.id=id;
  6. }
  7. public int getId(){
  8. return id;
  9. }
  10. //结果
  11. private Object response;
  12. //获取结果
  13. public Object get(int timeout) throws InterruptedException {
  14. synchronized (this){
  15. long begin = System.currentTimeMillis();
  16. //定义经历的时间
  17. long passedTime = 0;
  18. while(response==null){
  19. long waitTime = timeout-passedTime;
  20. //经历的时间超过了最大等待时间,退出循环
  21. if(waitTime<=0){
  22. break;//跳出循环,不再等待
  23. }
  24. this.wait(waitTime);
  25. passedTime = System.currentTimeMillis()-begin;//获得经历时间
  26. }
  27. }
  28. return response;
  29. }
  30. public void complete(Object response){
  31. synchronized (this){
  32. //给结果成员变量赋值
  33. this.response=response;
  34. this.notifyAll();
  35. }
  36. }
  37. }

解耦类MailBoxes/Futures

  1. class MailBoxes{
  2. private static Map<Integer,GuardedObject> boxes = new Hashtable<Integer,GuardedObject>();//用hashtable可以实现对boxex的访问互斥
  3. private static int id=1;
  4. //产生唯一id值,每次自增即可,多个线程可能都会调用id值,故操作id的方法需要加入synchronized
  5. public static synchronized int generateId(){
  6. return id++;
  7. }
  8. /*下边两个方法不需要加上synchronized,因为boxes的方法实现即同步*/
  9. public static GuardedObject createGuardedObject(){
  10. GuardedObject go = new GuardedObject(generateId());
  11. boxes.put(go.getId(),go);
  12. return go;
  13. }
  14. public static Set<Integer> getIds(){
  15. return boxes.keySet();
  16. }
  17. public static GuardedObject getGuardObject(int id){
  18. return boxes.remove(id);//使用remove,从集合中得到GuardedObject类对象后,即从集合中将其删除掉
  19. }
  20. }

居民类People与邮递员类Postman

  1. class People extends Thread{
  2. @Override
  3. public void run() {
  4. //收信
  5. GuardedObject guardedObject = MailBoxes.createGuardedObject();
  6. try {
  7. System.out.println("收信"+guardedObject.getId());
  8. Object mail = guardedObject.get(5000);//收信,不一定等够五秒
  9. System.out.println("收到信内容"+guardedObject.getId()+mail);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. class Postman extends Thread{
  16. //需要知道送信的信箱id值是什么、信的内容是什么
  17. private int id;
  18. private String mail;
  19. public Postman(int id,String mail){
  20. this.id=id;
  21. this.mail=mail;
  22. }
  23. @Override
  24. public void run() {
  25. GuardedObject guardedObject = MailBoxes.getGuardObject(id);
  26. System.out.println("送信"+id+mail);
  27. guardedObject.complete(mail);
  28. }
  29. }

注意这里两个类均继承Thread类,代表类的每个实例都是一个线程对象

测试代码如下:

  1. class Test20{
  2. public static void main(String[] args) throws InterruptedException {
  3. for (int i=0;i<3;i++){
  4. new People().start();
  5. }
  6. Thread.sleep(1000);
  7. for (Integer id:MailBoxes.getIds()){
  8. new Postman(id,"内容"+id).start();
  9. }
  10. }
  11. }

这里每个Person类和Postman类的对象即为一个线程,相当于先创建三个People线程等待收件,隔1秒后,再创建三个Postman线程去送件。

本例注意点:

还是需要注意多线程访问共享变量的问题,所以如果方法内涉及对共享变量的访问,需要在方法上加synchronized关键字,例子中用到的HashTable类内部的方法实现已经加了synchronized,属于不可变类,所以不需要显式地加上synchronized。