一,java多线程使用Object对象的wait,notify或者notifyAll方法进行通信
java的Object对象提供wait,notify和notifyAll方法。根据api中Object对象描述-------这三个方法调用时,应该只由对象监视器的所有者调用。即只能由synchronized所获得的锁对象进行调用,且只能在synchronized同步方法或者同步代码块中调用。
- 使用synchronized同步代码块时,同步锁对象为括号内的同步锁对象,必须使用该对象来调用这三个方法
- 使用synchronized修饰的同步方法时,同步锁对象为该类的默认实例,即this所指代的对象。对于静态类,锁对象为该类的字节码文件
<span style="font-size:14px;">package thread;
/*
* 假设系统中有多条线程,这些线程分别代表取钱者和存钱者。现在系统有一种特殊的要求,
* 系统要求存款者和取钱者不断的实现存款和取钱动作,而且要求每当存款者将钱存入指定账户后,
* 取钱者立即将钱取走.不允许存款者两次存钱,也不允许取钱者两次取钱.
* 我们通过设置一个旗标来标识账户中是否已有存款,有就为true,没有就标为false。
*/
public class Draw {
public static void main(String[] args) {
Acount acount = new Acount();
new Thread(new Custom(acount, 800), "fupeng").start();
new Thread(new Producer(acount, 800), "xiaoming1").start();
new Thread(new Producer(acount, 800), "xiaoming2").start();
new Thread(new Producer(acount, 800), "xiaoming3").start();
new Thread(new Producer(acount, 800), "xiaoming4").start();
new Thread(new Producer(acount, 800), "xiaoming5").start();
}
}
class Custom implements Runnable{
private Acount acount;
private int drawMoney;
public Custom(Acount acount,int drawMoney){
this.acount = acount;
this.drawMoney = drawMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.drawMoney(drawMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Acount acount;
private int saveMoney;
public Producer(Acount acount, int saveMoney) {
super();
this.acount = acount;
this.saveMoney = saveMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.saveMoney(saveMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Acount{
private int money;
private boolean flag = false;
public synchronized void saveMoney(int saveMoney) {
if(flag){
try {
System.out.println(Thread.currentThread().getName()+"wait并释放acount对象锁");
this.wait();
System.out.println("继续运行");
}catch (InterruptedException e){
e.printStackTrace();}
}
this.money+=saveMoney;
System.out.println(Thread.currentThread().getName()+"存入:"+saveMoney);
flag = true;
this.notifyAll();
}
public synchronized void drawMoney(int drawMoney){
if(!flag){
try {
System.out.println(Thread.currentThread().getName()+" wait并释放acount对象锁");
wait();
System.out.println("继续运行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.money-=drawMoney;
System.out.println(Thread.currentThread().getName()+"取出:"+drawMoney);
System.out.println("余额:"+money);
flag = false ;
notifyAll();
}
}</span>
在上述代码示例中,存在一个消费者线程,5个生产者线程。按照要求应该是:当有一个生产者线程存入钱之后,其他生产者线程不能再存入,只能等消费者取出之后再行存入。但是按照上述代码,运行中出现多个线程同时连续存入:
<strong>fupeng wait并释放acount对象锁
xiaoming5存入:800
继续运行
fupeng取出:800
余额:0
xiaoming1存入:800
xiaoming2wait并释放acount对象锁
xiaoming4wait并释放acount对象锁
xiaoming3wait并释放acount对象锁
xiaoming1wait并释放acount对象锁
xiaoming5wait并释放acount对象锁
fupeng取出:800
余额:0
继续运行
xiaoming5存入:800
继续运行
xiaoming1存入:800
继续运行
xiaoming3存入:800
继续运行
xiaoming4存入:800
继续运行
xiaoming2存入:800
fupeng取出:800
余额:3200</strong>
当消费者取出之后,5个生产者线程连续存入。
分析结果:当xiaoming1线程存入800之后,xiaoming2........xiaoming5执行存入操作,但flag标志为true,执行wait方法,释放同步锁,进入等待线程集。当fupeng线程取出之后,唤醒所有生产者线程,生产者线程接着获取最新状态同步锁对象,并接着从上次执行wait方法的地方接着继续执行,故出现1,2,3,4,5连续存入的现象。
注:在java多线程通信的方法中,当使用Object的wait,notify,notifyAll方法时:
若线程执行wait方法,该线程释放同步锁对象,并进入等待状态。当该线程被其他线程唤醒之后,重新获取最新状态的同步锁对象,并接着上次执行wait方法的地方继续执行。
修改方法:
上述代码执行之后出现连续存入现象,全因为线程执行wait方法进入等待线程集之后,再被唤醒之后是继续接着上次wait方法的地方执行,故将wait后的代码放入else代码块中即可。
public class Draw {
public static void main(String[] args) {
Acount acount = new Acount();
new Thread(new Custom(acount, 800), "fupeng").start();
new Thread(new Producer(acount, 800), "xiaoming1").start();
new Thread(new Producer(acount, 800), "xiaoming2").start();
new Thread(new Producer(acount, 800), "xiaoming3").start();
new Thread(new Producer(acount, 800), "xiaoming4").start();
new Thread(new Producer(acount, 800), "xiaoming5").start();
}
}
class Custom implements Runnable{
private Acount acount;
private int drawMoney;
public Custom(Acount acount,int drawMoney){
this.acount = acount;
this.drawMoney = drawMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.drawMoney(drawMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Acount acount;
private int saveMoney;
public Producer(Acount acount, int saveMoney) {
super();
this.acount = acount;
this.saveMoney = saveMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.saveMoney(saveMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Acount{
private int money;
private boolean flag = false;
public synchronized void saveMoney(int saveMoney) {
if(flag){
try {
System.out.println(Thread.currentThread().getName()+"wait并释放acount对象锁");
this.wait();
System.out.println(Thread.currentThread().getName()+"继续运行");
}catch (InterruptedException e){
e.printStackTrace();}
}else{
this.money+=saveMoney;
System.out.println(Thread.currentThread().getName()+"存入:"+saveMoney);
flag = true;
this.notifyAll();
}
}
public synchronized void drawMoney(int drawMoney){
if(!flag){
try {
System.out.println(Thread.currentThread().getName()+" wait并释放acount对象锁");
wait();
System.out.println(Thread.currentThread().getName()+"继续运行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
this.money-=drawMoney;
System.out.println(Thread.currentThread().getName()+"取出:"+drawMoney);
System.out.println("余额:"+money);
flag = false ;
notifyAll();
}
}
}
执行结果:
fupeng wait并释放acount对象锁
xiaoming2存入:800
fupeng继续运行
xiaoming1wait并释放acount对象锁
xiaoming3wait并释放acount对象锁
xiaoming4wait并释放acount对象锁
xiaoming5wait并释放acount对象锁
xiaoming2wait并释放acount对象锁
fupeng取出:800
余额:0
xiaoming2继续运行
xiaoming5继续运行
xiaoming4继续运行
xiaoming3继续运行
xiaoming1继续运行
xiaoming2存入:800
xiaoming1wait并释放acount对象锁
xiaoming3wait并释放acount对象锁
xiaoming4wait并释放acount对象锁
xiaoming5wait并释放acount对象锁
fupeng取出:800
余额:0
xiaoming2线程存入之后,生产者线程进入等待状态, 消费者取出钱之后notifyAll生产者线程,从上次wait方法的地方接着执行,执行完上次被打断的saveMoney方法。
接着xiaoming2线程又获取cpu执行资源,存入钱。。。。。。