wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。
wait函数必须在同步代码块中调用(也就是当前线程必须持有对象的锁),他的功能是这样的:
我累了,休息一会儿,对象的锁你们拿去用吧,CPU也给你们。
调用了wait函数的线程会一直等待,直到有其他线程调用了同一个对象的notify或者notifyAll方法才能被唤醒,需要注意的是:被唤醒并不代表立即获得对象的锁。也就是说,一个线程调用了对象的wait方法后,他需要等待两件事情的发生:
- 有其他线程调用同一个对象的notify或者notifyAll方法
- 被唤醒之后重新获得对象的锁
而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠
期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。
wait()和sleep()最大的不同在于:wait()会释放对象锁,而sleep()不会!
notify()/notifyAll()区别:
notify(): 该方法会唤醒因为调用对象的wait()而等待的线程,其实就是对对象锁的唤醒,从而使得wait()的线程可以有机会获取对象锁。(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还 在别人手里,别人还没释放)。调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。释放对象锁后,紧接着JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码。需要注意的是,wait()和notify()必须在synchronized代码块中调用。
notifyAll()则是唤醒所有等待的线程。
为了说明这一点,举例如下:
两个线程依次打印"A""B",总共打印10次:
public class Test {
public static final Object obj = new Object();
public static void main(String[] args) {
new Thread( new Produce()).start();
new Thread( new Consumer()).start();
}
}
public class Produce implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
int count = 10;
while (count > 0) {
//用Test类中的public static final Object obj = new Object();对象作为锁,
//目的是:使得线程A 线程B公用一把锁
synchronized (Test.obj) {
//System.out.print("count = " + count);
System.out.println("A");
count--;
/**
notify( )方法只会通知等待队列中的第一个相关线程(例子中只有2个线程 实际开发中 可能有多个线程)
(不会通知优先级比较高的线程),当调用Test. obj.notify后,Produce 调用线程依旧持有obj锁,
因此,Consumer 线程虽被唤醒,但是Consumer 仍无法获得obj锁。直到Produce 调用线程退出synchronized块,
释放obj锁后,Consumer 线程才获得锁继续执行。
*/
Test.obj.notify();
try {
/**
释放占有的对象锁,调用了wait函数的线程会一直等待,直到有其他线程(Consumer 线程)
调用了同一个对象(Test. obj)的notify方法才能被唤醒
*/
Test.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Consumer implements Runnable {
@Override
public synchronized void run() {
// TODO Auto-generated method stub
int count = 10;
while(count > 0) {
//用Test类中的public static final Object obj = new Object();对象作为锁
//目的是:使得线程A 线程B公用一把锁
synchronized (Test. obj) {
System. out.println( "B");
count --;
/**
notify( )方法只会通知等待队列中的第一个相关线程(例子中只有2个线程 实际开发中 可能有多个线程)
(不会通知优先级比较高的线程),当调用Test. obj.notify后,Consumer调用线程依旧持有obj锁,
因此,Produce 线程虽被唤醒,但是Produce 仍无法获得obj锁。直到Consumer 调用线程退出synchronized块,
释放obj锁后,Produce 线程才获得锁继续执行。
*/
Test. obj.notify();
try {
/**
释放占有的对象锁,调用了wait函数的线程会一直等待,
直到有其他线程(Produce线程)调用了同一个对象(Test. obj)的notify方法才能被唤醒
*/
Test. obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
打印结果:
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
通过上述步骤,相信大家已经明白这两个方法的使用了,但该程序还存在一个问题,当while循环不满足条件时,肯定会有线程还在等待资源,所以主线程一直不会终止。当然这个程序的目的仅仅为了给大家演示这两个方法怎么用。
由于synchronized圈的代码块 执行完成 就会释放对象锁 所以 可以改成这样 主线程就会停止了:
package org.luzhen.test;
public class Test1 {
public static final Object obj = new Object();
public static void main(String[] args) {
new Thread( new Produce()).start();
new Thread( new Consumer()).start();
}
}
package org.luzhen.test;
public class Produce implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
int count = 10;
while (count > 0) {
//用Test类中的public static final Object obj = new Object();对象作为锁,
//目的是:使得线程A 线程B公用一把锁
synchronized (Test1.obj) {
//System.out.print("count = " + count);
System.out.println("A");
count--;
}
}
}
}
package org.luzhen.test;
public class Consumer implements Runnable {
@Override
public synchronized void run() {
// TODO Auto-generated method stub
int count = 10;
while(count > 0) {
//用Test类中的public static final Object obj = new Object();对象作为锁
//目的是:使得线程A 线程B公用一把锁
synchronized (Test1. obj) {
System. out.println( "B");
count --;
}
}
}
}
输出结果:
A
A
A
A
A
A
A
A
A
B
B
B
B
B
B
B
B
B
B
Process finished with exit code 0
可以看到 主线程停止了: