并发编程 synchronized (七) wait / notify

目录

wait / notify

介绍

 wait 与 sleep 对比

保护性暂停(join 原理)

定义 

实现

join 实现(带超时的 Guarded Object)

生产者/消费者 

定义

实现 


wait / notify

介绍

① wait:当 Thread3、Thread4 获得锁时,由于没有达到执行条件(Thread3 要锤子,Thread4 要扳手);于是调用 wait,进入左边 WaitSet 等待(等人送工具,没人送会一直等) 

② notify:Thread1送工具(锤子);调用 notify,在两人中随机叫了一个;叫的是 Thread4 ,他进入 EntryList ;再次获得锁时,发现送来的工具是锤子又干不了活 ......

③ notifyAll:Thread1 送工具时,把所有人都叫去 EntryList

综上:wait 在线程获取锁,但是不符合执行条件时调用;notify 只会随机唤醒一个线程;notifyAll 会唤醒所有线程

 wait 与 sleep 对比

相同点:都会阻塞、释放时间片

不同点:① wait 会将锁释放,sleep 不会 sleep 是 Thread 方法,wait 是 Object 方法 ③ wait(无参)需要被唤醒;sleep 时间到了,自动解除阻塞

1. lazy 拿到锁,不干活,睡觉;所有人都要等着他醒来 

@Slf4j(topic = "c.WaitAndSleep")
public class WaitAndSleep {
    public static void main(String[] args) {
        sleepTest();
    }

    static void sleepTest(){
        new Thread(() -> {
            synchronized (WaitAndSleep.class){
                log.debug("懒得动,睡觉......"); // 懒狗,拿到锁,不干活
                try {
                    Thread.sleep(3000);
                    log.debug("唉,还没睡够......");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "lazy").start();

        for(int i = 0; i < 5; i++){
            new Thread(() -> {
                synchronized (WaitAndSleep.class){
                    log.debug("赶紧干完活,下班!");
                }
            }, "t" + i).start();
        }
    }
}

2. smart 虽然很累,但是不想给他人添麻烦,于是到隔壁 WaitSet 休息等人叫醒(可能没人叫)再去 EntryList 竞争锁

@Slf4j(topic = "c.WaitAndSleep")
public class WaitAndSleep {
    public static void main(String[] args) {
        waitTest(true);
//        waitTest(false);
    }

    static void waitTest(boolean isNotify){
        new Thread(() -> {
            synchronized (WaitAndSleep.class){
                log.debug("有点累,去隔壁WaitSet休息一下......");
                try {
                    WaitAndSleep.class.wait();
                    log.debug("有人叫我了,该去EntryList了......");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "smart").start();

        for(int i = 0; i < 5; i++){
            int index = i;
            new Thread(() -> {
                synchronized (WaitAndSleep.class){
                    log.debug("赶紧干完活,下班!");
                    if(index == 4 && isNotify){
                        WaitAndSleep.class.notify(); // 已经知道只有 smart 在 WaitSet
                    }
                }
            }, "t" + index).start();
        }
    }
}

 有人叫

 没人叫,会一直阻塞

3. smart 学聪明了;不能像上次一样,一直不醒了;定了个闹钟,就算没人叫,也能醒来

WaitAndSleep.class.wait(3000);
log.debug("闹钟响了,该去EntryList了......");

保护性暂停(join 原理)

定义 

Guarded Suspension,用在一个线程等待另一个线程的执行结果;该为同步模式

① 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 Guarded Object

② JDK 中,join、Future 的实现,采用的是此模式

③ 因为要等待另一方的结果,因此归类到同步模式

实现

public class GuardedObject {
    private Object value;

    public Object getValue() throws InterruptedException {
        synchronized (this){ // wait / notify
            while(value == null){
                this.wait();
            }
            return this.value;
        }
    }

    public void setValue(Object value) {
        synchronized (this){
            this.value = value;
            this.notifyAll();
        }
    }
}

@Slf4j(topic = "c.Test11")
class Test11{
    public static void main(String[] args) {
        GuardedObject go = new GuardedObject();
        new Thread(() -> {
            try {
                log.debug("尝试获取 go 的值...");
                Object value = go.getValue();
                log.debug("获取成功: " + value);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                log.debug("三秒后完成任务");
                Thread.sleep(3000);
                go.setValue("go");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

join 实现(带超时的 Guarded Object)

public Object getValue(long waitTime) throws InterruptedException {
    synchronized (this){ // wait / notify
        long start = System.currentTimeMillis(); // 开始等待的时间点
        long hasWaited = 0; // 已经等待了多久
        while(value == null){
            long restWaitTime = waitTime - hasWaited; // 剩余需要等待的时间
            if(restWaitTime <= 0){ // 小于 0 结束等待
               log.debug("即将退出,value 此时还是 null 吗? {}", value == null); // 超时时打印
               break;
            }
            log.debug("value 依旧为 null,{}s 后,自动退出", restWaitTime);
            this.wait(restWaitTime);
            hasWaited = System.currentTimeMillis() - start; // 更新已经等待的时间
        }
        return this.value;
   }
}

 未超时情况

@Slf4j(topic = "c.Test11")
class Test11{
    public static void main(String[] args) {
        GuardedObject go = new GuardedObject();
        new Thread(() -> {
            try {
                log.debug("尝试获取 value 的值,不想等太久...");
                Object value = go.getValue(5000);
                log.debug("已经退出了,查看 value 值: " + value);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                // 过一秒再给值
                Thread.sleep(1000);
                log.debug("过了一秒了,给你 value: null");
                go.setValue(null);
                // 过一秒再给值
                Thread.sleep(1000);
                log.debug("又过一秒了,给你 value: null");
                go.setValue(null);
                // 过一秒再给值
                Thread.sleep(1000);
                log.debug("又又过一秒,给你 value: go");
                go.setValue("go");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

超时情况 

Object value = go.getValue(2000);

join 源码(需要被等待的这个线程,是否存活就是需要传给其他线程的值)

public final synchronized void join(long millis)throws InterruptedException {
    long base = System.currentTimeMillis(); // 开始等待时间
    long now = 0; // 已经等待时间

    if (millis < 0) { 
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) { // 为 0
       while (isAlive()) { // 当前线程还存活
              wait(0); // 一直等
       }
    } else {
       while (isAlive()) {
             long delay = millis - now; // 剩余等待时间
             if (delay <= 0) { // 剩余等待时间小于 0 则不再等待
                 break;
             }
             wait(delay); // 执行等待
             now = System.currentTimeMillis() - base; // 更新已经等待时间
       }
    }
}

生产者/消费者(阻塞队列) 

定义

① 不需要产生结果和消费结果的线程一一对应

消费队列可以用来平衡生产和消费的线程资源(没被消费的,先暂存)

③ 生产者只负责生产消息,消费者只负责对消息的处理

④ JDK 各种阻塞队列都是用的这种模式(空了,就不消费;满了,就不生产)

⑤ 此方法为异步

实现 

 

@Slf4j(topic = "c.Test10")
class Test10{
    public static void main(String[] args) {
        MessageQueue mq = new MessageQueue(2);
        for(int i = 0; i < 5; i++){
            int index = i;
            new Thread(() -> {
                mq.put(new MyMessage(index, "消息" + index));
            }, "生产者" + i).start();
        }

        new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                MyMessage message = mq.take();
                log.debug("消费一条消息: " + message);
            }
        }, "消费者").start();
    }
}

@Slf4j(topic = "c.MessageQueue")
public class MessageQueue {
    private LinkedList<MyMessage> mq = new LinkedList();
    private int capacity;
    private int size;

    public MessageQueue(int capacity){
        this.capacity = capacity;
    }

    public MyMessage take(){
        synchronized (mq){
            while(size == 0){
                try {
                    log.debug("还没有 message 可消费......");
                    mq.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            size--;
            mq.notifyAll();
            return mq.pollFirst();
        }
    }

    public void put(MyMessage newMessage){
        synchronized (mq){
            while(size >= capacity){
                log.debug("空间已满......");
                try {
                    mq.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            size++;
            mq.add(newMessage);
            log.debug("生产一条信息消息: " + newMessage);
            mq.notifyAll();
        }
    }
}

class MyMessage{
    private int id;
    private Object value;

    public MyMessage(int id, Object value) {
        this.id = id;
        this.value = value;
    }

    public int getId() {
        return id;
    }

    public Object getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "MyMessage{" +
                "id=" + id +
                ", value=" + value +
                '}';
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值