wait()和notify()、notifyAll()

来源:互联网 发布:知乎数据接口 编辑:程序博客网 时间:2024/05/16 17:18

今天想到了这个问题(wait()方法、notify()、notifyAll()这三个方法是不是执行了就释放锁呢?答案是:都会释放锁

  1. 为什么 wait(), notify()和 和 notifyAll()必须在同步方法或者同步块中 必须在同步方法或者同步块中被调用?
    答:当一个线程需要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的 notify()方法。同样的,当一个线程需要调用对象的 notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

另外,notify()可能会引发死锁问题,而notifyAll()不会,具体参照点击进入

==========================
下面讲一个在练习的时候发现的知识点:
下面是一段生产者消费者模式程序:
Account类:

package cn.review.waitNotify.three;public class Account {    private String accountNo;    private Double balance;    public Account(String accountNo, double balance){        this.accountNo = accountNo;        this.balance = balance;    }    private boolean flag_draw;    public String getAccountNo() {        return accountNo;    }    public double getBalance() {        return balance;    }    //取钱    public synchronized void draw(double money){        if(!flag_draw){  //如果flag_draw表名账户还没人存进去            try {                balance.wait();   //锁的是整个account对象            } catch (InterruptedException e) {                e.printStackTrace();            }              }else{            balance = balance - money;            System.out.println(Thread.currentThread().getName()+" 取出"+money+"元,余额是"+balance+"元");            flag_draw = false;            balance.notifyAll();        }    }    //存钱    public synchronized void deposit(double money){        if(flag_draw){            try {                balance.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }else{            balance = balance + money;            System.out.println(Thread.currentThread().getName()+" 存入"+money+"元,余额是"+balance+"元");            flag_draw = true;            balance.notifyAll();        }    }}

生产者(存钱的人)类:

package cn.review.waitNotify.three;public class Producer implements Runnable{    private Account account;    private double depositMoney;    public Account getAccount() {        return account;    }    public double getDepositMoney() {        return depositMoney;    }    public void setDepositMoney(double depositMoney) {        this.depositMoney = depositMoney;    }    public Producer(Account account, double depositMoney){        this.account = account;        this.depositMoney = depositMoney;    }    @Override    public void run() {        //进行100次存钱        for(int i = 0; i < 100; i++){            account.deposit(depositMoney);        }    }}

消费者(取钱的人)类:

package cn.review.waitNotify.three;public class Consumer implements Runnable{    private Account account;    private double drawMoney;    public Consumer(Account account, double drawMoney){        this.account = account;        this.drawMoney = drawMoney;    }    public Account getAccount() {        return account;    }    public double getDrawMoney() {        return drawMoney;    }    @Override    public void run() {        //进行100次取款        for(int i = 0; i < 100; i++){            account.draw(drawMoney);        }    }}   

测试类:

package cn.review.waitNotify.three;public class Test1 {    public static void main(String[] args) {        Account account = new Account("212212", 1000);        Producer p1 = new Producer(account, 100);        Consumer c1 = new Consumer(account, 80);        Consumer c2 = new Consumer(account, 80);        new Thread(p1, "存钱者").start();        new Thread(c1, "取前者1").start();        new Thread(c2, "取前者2").start();    }}

测试类中另开3条线程,其中1条生产者,2条消费者。每个线程都进行100次的存钱或取钱,在Account类中保证了一个账户不能进行连续的存钱或者取钱,运行得到的结果是:

存钱者 存入100.0元,余额是1100.0元取前者2 取出80.0元,余额是1020.0元存钱者 存入100.0元,余额是1120.0元取前者1 取出80.0元,余额是1040.0元存钱者 存入100.0元,余额是1140.0元取前者2 取出80.0元,余额是1060.0元存钱者 存入100.0元,余额是1160.0元取前者1 取出80.0元,余额是1080.0元存钱者 存入100.0元,余额是1180.0元取前者2 取出80.0元,余额是1100.0元存钱者 存入100.0元,余额是1200.0元取前者1 取出80.0元,余额是1120.0......

程序最后处于不输出任何数据状态(注意这不是死锁)。。

以上一切都很正常,当我把Account类的取钱方法和存钱方法改为:
取钱方法:

    //取钱    public void draw(double money){        synchronized (balance) {            if(!flag_draw){  //如果flag_draw表名账户还没人存进去                try {                    balance.wait();   //锁的是整个account对象                } catch (InterruptedException e) {                    e.printStackTrace();                }                  }else{                balance = balance - money;                System.out.println(Thread.currentThread().getName()+" 取出"+money+"元,余额是"+balance+"元");                flag_draw = false;                balance.notifyAll();            }        }    }

存钱方法:

    //存钱    public void deposit(double money){        synchronized (balance) {            if(flag_draw){                try {                    balance.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }else{                balance = balance + money;                System.out.println(Thread.currentThread().getName()+" 存入"+money+"元,余额是"+balance+"元");                flag_draw = true;                balance.notifyAll();            }        }    }

则会出现以下结果:

存钱者 存入100.0元,余额是1100.0元Exception in thread "存钱者" java.lang.IllegalMonitorStateException    at java.lang.Object.notifyAll(Native Method)    at cn.review.waitNotify.three.Account.deposit(Account.java:53)    at cn.review.waitNotify.three.Producer.run(Producer.java:26)    at java.lang.Thread.run(Thread.java:619)

百度了下,报IllegalMonitorStateException的原因是:

首先你要了解这个异常为什么会抛出,这个异常会在三种情况下抛出:
1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;
2>当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
3>当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。

后来我把balance+=money去掉了,程序又恢复正常了。原因是这样的, 当线程进入临界区时,获得了balance的对象所,而在Account类中,balance是Double类型的,而Double类型是不可变类,它与String一样,也就是当进行balance+=money时,已经将balance的实例改变了,再也不指向原来的Double对象了,这时候进行balance.wait()balance.notify()balance.notifyAll(); 就自然会报错。

重点记下:Double、Integer、Character等是不可变类。wait()、notify()、notifyAll()在同步代码块里面使用,

0 0
原创粉丝点击