Day04 【学习日记】 并发编程模式-同步模式之保护性暂停

36 阅读2分钟

1.定义

即Guarded Suspension,用在一个线程等待另一个线程的执行结果 要点

  • 有一个结果需要从一个线程传递到另外一个线程,让他们关联一个GuardedObject
  • 如果有结果不断一个线程到另外一个线程那么可以使用消息队列(见生产者/消费者)
  • JDK中,join的实现、Future的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式

image.png

2.实现

@Slf4j
public class GuardedObject {

    private static final Object lock = new Object();
    private Object response;

    public Object get() {
        synchronized (lock) {
            while (response == null) {
                try {
                    log.debug("wait......");
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            return response;
        }
    }

    public void complete(Object response) {
        synchronized (lock) {
            this.response = response;
            log.debug("notify......");
            lock.notifyAll();
        }
    }

    public static void main(String[] args) {
        GuardedObject guardedObject = new GuardedObject();
        new Thread(() -> {
            try {
                // 子线程执行下载
                List<String> response = download();
                log.debug("download complete...");
                guardedObject.complete(response);
            } catch (IOException e) {
                e.printStackTrace();
            }
        },"t1").start();
        log.debug("waiting...");
        // 主线程阻塞等待
        List<String> response = (List<String>) guardedObject.get();
        log.debug("get response: [{}] lines", (response).size());
    }


}

3.带超时版的GuardedObject

@Slf4j
public class GuardedObjectTimeOut {

    private static final Object lock = new Object();
    private Object response;

    public Object get(long millis) {
        synchronized (lock) {
            long begin = System.currentTimeMillis();
            long timePassed = 0;
            while (response == null) {
                try {
                    long waitTime = millis - timePassed;
                    if (waitTime <= 0) {
                        log.debug("break.....");
                        break;
                    }
                    lock.wait(waitTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                timePassed = System.currentTimeMillis() - begin;
                log.debug("response is not null?:{}", response == null);
            }
            return response;
        }
    }

    public void complete(Object response) {
        synchronized (lock) {
            this.response = response;
            lock.notifyAll();
        }
    }

    public static void main(String[] args) {
        GuardedObjectTimeOut g = new GuardedObjectTimeOut();
        new Thread(() -> {
            try {
                // 子线程执行下载
                Thread.sleep(3000);
                log.debug("延迟等待……");
                g.complete(null);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1").start();


        log.debug("waiting...");
        // 主线程阻塞等待
        Object response = g.get(2000);

        log.debug("response :【{}】", response);
    }

}

4.多任务版GuardedObject

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

image.png

@Slf4j
public class GuardedObjectMulti {

    private int id;

    private Object response;

    public GuardedObjectMulti(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public Object get(long timeout) {
        synchronized (this) {
            long begin = System.currentTimeMillis();

            long passedTime = 0;
            while (response == null) {
                long waitTime = timeout - passedTime;
                if (waitTime <= 0) {
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                passedTime = System.currentTimeMillis() - begin;
            }
            return response;
        }
    }

    public void complement(Object response) {
        synchronized (this) {
            this.response = response;
            this.notifyAll();
        }
    }
}


class Mailboxes {
    private static Map<Integer, GuardedObjectMulti> map = new Hashtable<>();

    private static int id = 1;

    // 产生唯一id
    private static synchronized int generateId() {
        return id++;
    }

    public static GuardedObjectMulti createGuarded() {
        GuardedObjectMulti guarded = new GuardedObjectMulti(generateId());
        map.put(guarded.getId(), guarded);
        return guarded;
    }

    public static GuardedObjectMulti getGuarded(int id) {
        return map.remove(id);
    }

    public static Set<Integer> getIds() {
        return map.keySet();
    }

}

@Slf4j
class People extends Thread {
    @Override
    public void run() {
        GuardedObjectMulti guarded = Mailboxes.createGuarded();
        log.debug("开始收信 id:{}", guarded.getId());
        Object response = guarded.get(5000);
        log.debug("收信内容 response:{}", response);
    }
}

@Slf4j
class Postman extends Thread {
    private final int id;
    private final String mail;

    public Postman(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
        GuardedObjectMulti guarded = Mailboxes.getGuarded(id);
        log.debug("id:{},mail:{}", id, mail);
        guarded.complement(mail);
    }
}

class Test {
    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();
        }
    }
}