6-6 jmu-Java-07多线程-同步访问 (10 分)
现已有Account
类,拥有
属性:
private int balance
方法:
相应的getter方法。
要求为该类编写:
void deposit(int money)
//存钱,在余额的基础上加上money
void withdraw(int money)
//取钱,在余额的基础上减去money
注意:
- 取钱时如果balance<0的时候,会抛出异常。在多线程情况下,如只有一个存钱的线程,但是有多个取钱的线程,很可能会抛出异常。
- 需要编写完整的deposit方法与withdraw的前半部分代码解决该问题。
裁判测试程序:
import java.util.Scanner;
//这里是已有的Account类前半部分的代码
/*这里是deposit代码*/
/*这里是withdraw代码的前半部分*/
if(balance<0) //这里是withdraw代码的后半部分。
throw new IllegalStateException(balance+"");
}
/*系统已有代码,无需关注*/
输入样例:
分别为初始余额、存钱次数、存钱金额
取钱次数、取钱金额。有3个线程。
0 100000 12
100000 4
输出样例:
余额:使用线程跑出的结果。
余额:存钱次数存钱金额 - 取钱次数取钱金额*3
0
0
我的实现代码:
public void deposit(int money) {
synchronized (this) {
balance += money;
notifyAll();
}
}
public void withdraw(int money) {
synchronized (this) {
while (balance < money) {
try {
wait();
} catch (InterruptedException e) {
}
}
balance -= money;
//notifyAll();
}
题意分析:
本题是典型的生产者消费者问题,涉及到同步访问和线程交互的问题。
- 本题应有如下要求:
- 在同一时刻,只能执行取钱或者存钱操作中的任何一个,不允许同时取钱和存钱。
- 不允许超额取钱,必须在余额的数量比所要取用的钱的数量更大的情况下才允许取钱。
- 当余额的数量比所要取用的钱的数量更小的时候,当前的取钱的这条线程应该暂时暂停下来。
- 由于无法确定究竟什么时候余额超过所需要取钱的数额,所以干脆没存入一次钱就都通知所有的取钱队列开始准备进入可以取钱的流程,而至于能顺利取到钱还是得继续等待,就交给要求2去判断就行。
** 总结相关的知识要点。**
synchronized
修饰词可以修饰的对象有三类:
- 第一类是修饰类内的非静态方法,这种条件下,作用的范围是该类的一个实例,效果是同一时间,该对象的那个实例的所有同步方法,一次只能有一个方法被使用。
- 第二类是修饰类内的非静态方法的一部分语句,就如同本题的效果,这样做的话,同步限制的范围会更为精确,程序的运行效率会更高。
- 第三类是修饰类内的静态方法,这种情况作用范围会扩大到这个类的所有实例中,在同一时间内,仅能有一个实例的一个同步方法被访问。
wait()、notify() and notifyAll().
这三个语句实现线程之间的相互交流。
wait()
- 使得当前的线程进入等待的序列,直到它被其他的线程用notify()或者notifyAll()唤醒,它被用来让线程等待某一个条件。
- 它必须在同步区域内部被使用。
- 始终应该使用wait循环模式来调用wait方法,永远不要在循环之外调用wait方法:循环会在等待之前和之后测试条件。
- 在等待之前测试条件,当条件已经成立时立即就跳过等待,这对确保活性时必要的。
- 在等待之后测试条件,如果条件不成立的话继续等待,这对于确保安全性时必要的。
- 在等待之后依然需要不断地监视所等待的条件是否成立,这是等待需要使用循环语句的一个最关键的原因。
- 使用
wait()
语句的一般格式:
public void method(参数) {
synchronized (this) {
while ( !condition(条件不满足的时候)) {
wait();
}
}
notify()
- 唤醒一个在当前的对象上等待的线程,如果有多个线程在当前的对象上等待,那就任意选择其中的一个来唤醒。
- 应该指出的是,调用notify()实际上并没有放弃对资源上的锁。它告诉等待线程可以唤醒。然而,如果对某一资源调用notify(),但同步块内的资源的执行还需要进行10秒,那么线程需要等待额外的10秒,对象上的锁被释放,被唤醒的线程才能执行。
notifyAll()
- 唤醒所有在当前对象上等待的线程。
- 这并不会影响程序的正确型,因为它可以让条件已经满足的线程往下运行,而对于暂时条件还不满足的线程,他们本身的wait循环结构会让他们继续等待,直到条件被满足。