【JUC并发】黑马程序员:wait/notify正确姿势

1.同步模式-普通版

定义GuardedObject1

class GuardedObject1 {
    private Object response;
    private final Object lock=new Object();
    //获得消息
    public Object get() throws Exception{
        synchronized (lock){
            while (true){
                if(response==null){
                    lock.wait();
                }else {
                    return response;
                }
            }
        }
    }
    //生产消息
    public Object set(Object o){
        synchronized (lock){
            response=o;
            lock.notify();
            return o;
        }
    }
}

测试

一个线程等待另一个线程的执行结果

public static void main(String[] args) {
        GuardedObject guardedObject=new GuardedObject();
        log.debug("当前时间");
        new Thread(()->{
            try {
                log.debug(String.valueOf(guardedObject.get()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                Thread.sleep(1000);
                log.debug("设置消息");
                guardedObject.set("hello juc");
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
}

执行结果

00:58:50 [main] c.Demo4 - 当前时间
00:58:51 [t2] c.Demo4 - 设置消息
00:58:51 [t1] c.Demo4 - hello juc

2.同步模式-超时版

定义GuardedObject2

class GuardedObject2 {
    private Object response;
    private final Object lock=new Object();
    //获得消息
    public Object get(Long l) throws Exception{
        synchronized (lock){
            Long start=System.currentTimeMillis();
            Long now=0L;
            Long need=0l;
            while (true){
                now=System.currentTimeMillis();
                need=l-(now-start);
                if(need<=0){
                    log.debug("消息为null超时结束");
                    break;
                }
                if(response==null){
                    lock.wait(need);
                }else {
                    return response;
                }
            }
        }
        return "超时返回";
    }
    //生产消息
    public Object set(Object o){
        synchronized (lock){
            response=o;
            lock.notify();
            return o;
        }
    }
}

测试:没有超时

public static void main(String[] args) {
        GuardedObject2 guardedObject=new GuardedObject2();
        log.debug("当前时间");
        new Thread(()->{
            try {
                System.out.println(guardedObject.get(5000l));
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                Thread.sleep(4000);
                log.debug("设置消息");
                guardedObject.set("hello juc");
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
}

测试:超时

public static void main(String[] args) {
        GuardedObject2 guardedObject=new GuardedObject2();
        log.debug("当前时间");
        new Thread(()->{
            try {
                System.out.println(guardedObject.get(3000l));
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                Thread.sleep(4000);
                log.debug("设置消息");
                guardedObject.set("hello juc");
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
}

输出

00:50:37 [main] c.Demo4 - 当前时间
00:50:41 [t2] c.Demo4 - 设置消息
00:50:41 [t1] c.Demo4 - hello juc

00:51:04 [main] c.Demo4 - 当前时间
00:51:07 [t1] c.Demo3 - 消息为null超时结束
00:51:07 [t1] c.Demo4 - 超时返回
00:51:08 [t2] c.Demo4 - 设置消息

3.同步模式-多任务版

图中 Futures 就好比居民楼一层的信箱(每个信箱有房间编号),左侧的 t0,t2,t4 就好比等待邮件的居民,右 侧的 t1,t3,t5 就好比邮递员 。

如果需要在多个类之间使用 GuardedObject3对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类, 这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。
在这里插入图片描述
新增 id 用来标识 Guarded Object3

/**
 * 快递格子
 */
@Slf4j(topic = "c.four-Demo3")
class GuardedObject3{
    private Object response;
    private final Object lock=new Object();

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

    private int id;

    public int getId() {
        return id;
    }

    //获得消息
    public Object get(Long l) throws Exception{
        synchronized (lock){
            Long start=System.currentTimeMillis();
            Long now=0L;
            Long need=0l;
            while (true){
                now=System.currentTimeMillis();
                need=l-(now-start);
                if(need<=0){
                    log.debug("消息为null超时结束");
                    break;
                }
                if(response==null){
                    lock.wait(need);
                }else {
                    return response;
                }
            }
        }
        return "超时返回";
    }
    //生产消息
    public Object set(Object o){
        synchronized (lock){
            response=o;
            lock.notifyAll();
            return o;
        }
    }
}

中间解耦类

/**
 * 快递柜
 */
class Mailboxes{
    private static int i=0;
    static Map<Integer,GuardedObject3> m=new Hashtable<>();

    public static synchronized int getId(){
        return ++i;
    }
    //取件人创建快递格子
    public static GuardedObject3 create(){
        GuardedObject3 guardedObject3=new GuardedObject3(getId());
        m.put(guardedObject3.getId(),guardedObject3);
        return guardedObject3;
    }
    //送信人获取快递格子
    public static GuardedObject3 getGuardedObject3(int id){
        return m.remove(id);
    }
    //送信人获取格子集合
    public static synchronized Set<Integer> getIds(){
        return m.keySet();
    }
}

业务相关类

/**
 * 取件人
 */
@Slf4j(topic = "c.four-Demo3")
class Person extends Thread{
    @Override
    public void run(){
        GuardedObject3 guardedObject3 = Mailboxes.create();
        try {
            Object o = guardedObject3.get(3000l);
            log.debug("收到信:"+o);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 送信人
 */
class Postman extends Thread{
    private int id;
    Postman(int id){
        this.id=id;
    }
    @Override
    public void run() {
        GuardedObject3 guardedObject3 = Mailboxes.getGuardedObject3(id);
        guardedObject3.set("来信了"+id);
    }
}

测试

public static void main(String[] args)throws Exception{
        log.debug("开始");
        for (int i = 0; i < 5; i++) {
            new Person().start();
        }
        TimeUnit.SECONDS.sleep(2);
        for (Integer id : Mailboxes.getIds()) {
            new Postman(id).start();
        }
    }
}

某次运行结果

11:46:47 [main] c.four-Demo3 - 开始
11:46:49 [Thread-3] c.four-Demo3 - 收到信:来信了5
11:46:49 [Thread-1] c.four-Demo3 - 收到信:来信了4
11:46:49 [Thread-2] c.four-Demo3 - 收到信:来信了3
11:46:49 [Thread-4] c.four-Demo3 - 收到信:来信了2
11:46:49 [Thread-0] c.four-Demo3 - 收到信:来信了1

4.异步模式-生产者/消费者

1.定义

此处定义了解耦的队列,生产者不再需要等待消费者消费完,故称此模式为异步模式

要点

  • 与前面的保护性暂停中的 GuardObject 不同,不需要产生结果和消费结果的线程一一对应
  • 消费队列可以用来平衡生产和消费的线程资源
  • 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
  • 消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据
  • JDK 中各种阻塞队列,采用的就是这种模式

在这里插入图片描述

2.实现

/**
 * 消息对象
 */
class Message{
    private int id;
    private String message;

    public Message(int id, String message) {
        this.id = id;
        this.message = message;
    }

    public int getId() {
        return id;
    }

    public String getMessage() {
        return message;
    }
}

/**
 * 消息队列
 */
@Slf4j(topic = "c.four-Demo4")
class MessageQueue{
    private LinkedList<Message> linkedList;
    private int length;

    public MessageQueue(int length) {
        this.linkedList=new LinkedList<>();
        this.length=length;
    }
    //生产消息
    public void product(Message message) throws InterruptedException {
        synchronized (linkedList){
            while(linkedList.size()>=length){
                log.debug("库存积压");
                linkedList.wait();
            }
            log.debug("生产消息:"+message.getMessage());
            linkedList.add(message);
            linkedList.notifyAll();
        }
    }
    //消费消息
    public Message consumer() throws InterruptedException {
        synchronized (linkedList){
            while(linkedList.size()<=0){
                log.debug("没货了");
                linkedList.wait();
            }
            Message remove = linkedList.remove();
            log.debug("消费消息:"+remove.getMessage());
            linkedList.notifyAll();
            return remove;
        }
    }
}

3.测试

public static void main(String[] args) {
        MessageQueue messageQueue=new MessageQueue(2);
        for (int i = 0; i < 4; i++) {
            int id=i;
            new Thread(()->{
                try {
                    messageQueue.product(new Message(id,"消息"+id));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"product"+i).start();
        }
        new Thread(()->{
            while (true){
                try {
                    messageQueue.consumer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"consumer").start();
    }

某次运行结果

13:36:25 [product1] c.four-Demo4 - 生产消息:消息1
13:36:25 [consumer] c.four-Demo4 - 消费消息:消息1
13:36:25 [consumer] c.four-Demo4 - 没货了
13:36:25 [product0] c.four-Demo4 - 生产消息:消息0
13:36:25 [product2] c.four-Demo4 - 生产消息:消息2
13:36:25 [product3] c.four-Demo4 - 库存积压
13:36:25 [consumer] c.four-Demo4 - 消费消息:消息0
13:36:25 [consumer] c.four-Demo4 - 消费消息:消息2
13:36:25 [consumer] c.four-Demo4 - 没货了
13:36:25 [product3] c.four-Demo4 - 生产消息:消息3
13:36:25 [consumer] c.four-Demo4 - 消费消息:消息3
13:36:25 [consumer] c.four-Demo4 - 没货了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿你满腹经纶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值