调用线程的sleep,yield方法时,线程并不会让出对象锁,wait却不同。
wait函数必须在同步代码块中调用(也就是当前线程必须持有对象的锁),他的功能是这样的:
我累了,休息一会儿,对象的锁你们拿去用吧,CPU也给你们。
调用了wait函数的线程会一直等待,直到有其他线程调用了同一个对象的notify或者notifyAll方法才能被唤醒,需要注意的是:被唤醒并不代表立即获得对象的锁。也就是说,一个线程调用了对象的wait方法后,他需要等待两件事情的发生:
如果一个线程调用了某个对象的wait方法,但是后续并没有其他线程调用该对象的notify或者notifyAll方法,则该线程将会永远等下去…
notify/notifyAll方法也必须在同步代码块中调用(也就是调用线程必须持有对象的锁),他们的功能是这样的:
女士们,先生们请注意,锁的对象我即将用完,请大家醒醒,准备一下,马上你们就能使用锁了。
不同的是,notify方法只会唤醒一个正在等待的线程(至于唤醒谁,不确定!),而notifyAll方法会唤醒所有正在等待的线程。还有一点需要特别强调:调用notify和notifyAll方法后,当前线程并不会立即放弃锁的持有权,而必须要等待当前同步代码块执行完才会让出锁。
如果一个对象之前没有调用wait方法,那么调用notify方法是没有任何影响的。
下面来看一个例子:
package com.cooperation; import java.util.concurrent.TimeUnit; public class Test { public static Object object = new Object(); static class Thread1 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" get the lock."); } } } static class Thread2 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); object.notify(); System.out.println(Thread.currentThread().getName()+" invoke notify()"); System.out.println(Thread.currentThread().getName()+" release the lock."); } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Thread1()); Thread thread2 = new Thread(new Thread2()); thread1.start(); TimeUnit.SECONDS.sleep(1); thread2.start(); } }
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-1 release the lock. Thread-0 get the lock.
可以看到当Thread-0调用了wait方法后就释放了object锁,Thread-1获取锁之后调用notify的释放锁,但是这个时候Thread没有立刻获取object锁,而是等到了Thread-1的同步块退出之后才获取了object的锁。
如果将class Thread2中的 System.out.println(Thread.currentThread().getName()+" release the lock.");这句放在synchronized(object)的外面,有可能出现如下的运行结果:
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-0 get the lock. Thread-1 release the lock.
这是因为,当Thread-1释放了object锁并且退出了同步块之后Thread-0立刻获取了锁,而这时候两个线程的打印语句的顺序就随机了。