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元
可以看到,不会出现存钱者多存或者取钱者多取的情况。