wait,notify,notifyAll原理以及实际使用场景

wait : 使当前获取锁的线程挂起。可以选择wait(time,unit) 设置时间和时间单位。

notify : 使当前获取过锁并被wait过的线程由阻塞状态转为就绪状态(随机唤醒)。

notifyAll : 使当前获取过锁并被wait过的所有线程由阻塞状态转为就绪状态。

假设现在系统中有两个线程,这两个线程分别代表存款者和取钱者——现在假设系统有一种特殊的要求,系统要求存款者和取钱者不断地重复存款、取钱的动作,而且要求每当存款者将钱存入指定账户后,取钱者就立即取出该笔钱。不允许存款者连续两次存钱,也不允许取钱者连续两次取钱。

怎么实现呢? 其实可以使用wait,notify,notifyAll 来实现,notifyAll的话可以唤醒所有挂起的线程,取钱者的话我生成了多个例子,因此取钱者就用这个了。notify的话只能随机唤醒一个wait的线程,如果存钱者只有一个的话可以使用notify。但是要注意,一定要对wait过的线程才能使用notify或者notifyAll,否则没有任何意义。

看代码:

账户类

package com.zp.me.thread.wait;

import lombok.Data;

/**
 * FileName: Account
 * Author:  zp
 * Date:    2022/2022/8/30/20:40
 * Description:
 */
@Data
public class Account {
    private String accountNo;
    private double balance;

    public Account() {
    }

    public Account(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // 如果为true 则代表有存款,取款者可取款,反之存钱者可存钱
    private boolean flag = false;


    /***
     * @Description: 取钱
     * @Param: [money]
     * @return: void
     * @Author: zp
     * @Date: 2022/8/30
     */
    public synchronized void draw(double money) {
        try {
            if (flag) {
                balance = balance - money;
                System.out.println(Thread.currentThread().getName() + "取了" + money + "元");
                flag = false;
                notifyAll();
            } else {

                wait();

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /***
     * @Description: 存钱
     * @Param: [money]
     * @return: void
     * @Author: zp
     * @Date: 2022/8/30
     */
    public synchronized void deposit(double money) {
        try {
            if (flag) {

                wait();

            } else {
                balance = balance + money;
                System.out.println(Thread.currentThread().getName() + "存了" + money + "元");
                flag = true;
                notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

 取钱线程

package com.zp.me.thread.wait;

import lombok.Data;

/**
 * FileName: DrawThread
 * Author:  zp
 * Date:    2022/2022/8/30/20:54
 * Description:
 */
@Data
public class DrawThread extends Thread{
    private Account account;
    private double money;

    public DrawThread(String name,Account account, double money) {
        super(name);
        this.account = account;
        this.money = money;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            account.draw(money);
        }
    }
}

存钱线程

package com.zp.me.thread.wait;

import lombok.Data;

/**
 * FileName: DrawThread
 * Author:  zp
 * Date:    2022/2022/8/30/20:54
 * Description:
 */
@Data
public class DepositThread extends Thread{
    private Account account;
    private double money;

    public DepositThread(String name, Account account, double money) {
        super(name);
        this.account = account;
        this.money = money;
    }

    @Override
    public void run() {
        for (int i = 0; i < 1; i++) {
            account.deposit(money);
        }
    }
}

 测试类

package com.zp.me.thread.wait;

/**
 * FileName: ThreadTest
 * Author:  zp
 * Date:    2022/2022/8/30/21:46
 * Description:
 */
public class ThreadTest {
    public static void main(String[] args) {
        Account account = new Account("0000001", 0.00);
        new DepositThread("存钱者1",account,500).start();
        new DrawThread("取钱者1",account,500).start();
        new DrawThread("取钱者2",account,500).start();
        new DrawThread("取钱者3",account,500).start();

    }
}

结果:

Connected to the target VM, address: '127.0.0.1:61034', transport: 'socket'
存钱者1存了500.0元
取钱者3取了500.0元

可以看到,不会出现存钱者多存或者取钱者多取的情况。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值