wait()方法
wait()方法使得当前线程必须等待,等到另外一个线程调用notify()或者notifyAll()方法之后。线程调用wait()方法,即释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
当前的线程必须拥有当前对象的monitor,也即lock,就是锁。
要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
注意区别:
当线程调用了wait()方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
notify()方法
notify()方法会唤醒一个等待当前对象的锁的线程。
如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。
被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势, 对象的下一个线程还是需要通过一般性的竞争。
notify()方法应该是被拥有对象锁的线程所调用。
(This method should only be called by a thread that is the owner of this object's monitor.)
换句话说,和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。
wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。
一个线程变为一个对象的锁的拥有者是通过下列三种方法:
1.执行这个对象的synchronized实例方法。
2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。
3.对于Class类的对象,执行那个类的synchronized、static方法。
package thread.communication;
public class Account {
private String accountNo;
private double balance;
private boolean flag = true;// 存款
public Account() {
}
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public String getAccountNo() {
return this.accountNo;
}
public double getBalance() {
return this.balance;
}
public synchronized void take(double drawAmount) {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 执行取钱
try {
System.out.println("姓名:" + Thread.currentThread().getName()
+ " 取钱:" + drawAmount);
balance -= drawAmount;
System.out.println("账户余额为:" + balance);
// 标识账户为false。
flag = true;
// 唤醒其他线程
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void post(double depositAmount) {
while (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 执行存款
System.out.println("姓名:" + Thread.currentThread().getName() + " 存款:"
+ depositAmount);
balance += depositAmount;
System.out.println("账户余额为:" + balance);
flag = false;
// 唤醒其他线程
notifyAll();
}
}
-------------------------------------------------------------------------------------------------------------
package thread.communication;
public class DepositThread extends Thread {
// 模拟用户账户
private Account account;
// 当前取钱线程所希望存款的钱数
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
// 重复10次执行存款操作
public void run() {
for (int i = 0; i < 10; i++) {
account.post(depositAmount);
}
}
}
------------------------------------------------------------------------------------------------------------
package thread.communication;
public class DrawThread extends Thread {
// 模拟用户账户
private Account account;
// 当前取钱线程所希望取的钱数
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
// 重复10次执行取钱操作
public void run() {
for (int i = 0; i < 10; i++) {
account.take(drawAmount);
}
}
}
-----------------------------------------------------------------------------------------------
package thread.communication;
public class TestDraw {
public static void main(String[] args) throws InterruptedException {
// 创建一个账户
Account acct = new Account("1234567", 0);
Thread t1 = new DepositThread("卢俊义", acct, 8000);
Thread t2 = new DrawThread("鲁智深", acct, 8000);
Thread t3 = new DepositThread("宋江", acct, 1000);
Thread t4 = new DrawThread("李逵", acct, 1000);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
--------------------------运行结果部分如下:(因为每次运行结果顺序不一样,但是尽管顺序不一样,但是结果是正确的!)
读者查看这篇文章,里面有注意点,注意notify()和notifyAll() :http://www.cnblogs.com/mengdd/archive/2013/02/20/2917956.html ,, -------------------------