一、概述
一个
线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者就是消费者,这种模式隔离了“做什么”(what)和“怎么做”(How),在功能层面上实现了解耦,体系结构上具备了良好的伸缩性 ,Java就是通过等待和通知机制来实现这种功能的。
当一个线程完成了特定的环节时候,就去等待另外的线程执行,第二个线程准备好第一个线程需要的条件后,就去唤醒第一个线程,这样实现了两个线程的通信协作。
让线程停下来等待,可以通过Thread类的静态方法,Thread.sleep(millis); 也可以使用Object及其子类都具备的wait()/wait(millis)方法;两者的异同在下面展开
二、Thread.sleep() 和 Object.wait()的区别
1. 调用Thread.sleep(),当前线程不释放锁,睡眠过程中一直持有锁对象,睡眠时间过后继续执行;
2.调用wait()方法,线程释放锁对象,直到这个锁对象的notify或者notifyAll()被调用,才会被唤醒,重新争夺锁。
3. sleep()是Thread类的静态方法,wait/notify是Object类的通用方法,这也符合Java中锁的理念,对象都可以作为锁。
注意:
1. wait()方法只能用于同步代码块范围内,且只能对同步方法或者同步代码块持有的锁对象进行wait和notify操作,否则会报错IllegalMonitorStateException
2. 两者都可能抛出InterruptedException
三、show me the code
- import java.text.SimpleDateFormat;
import java.util.Date;
- public class TestSleepAndWait {
public static void main(String[] args) {
new Thread1().start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread2().start();
}
}
class Thread1 extends Thread{
private void sout(String s){
System.out.println(s+" "+new SimpleDateFormat("HH:mm:ss:SS").format(new Date()));
}
@Override
public void run() {
sout("enter Thread1.run");
synchronized (TestSleepAndWait.class){//wait只能在同步代码块或者同步方法中使用
sout("Thread1 is going to wait");
try {
TestSleepAndWait.class.wait(); // 这里只能使用持有锁TestSleepAndWait.class.wait(),使用其他对象则报错java.lang.IllegalMonitorStateException
} catch (InterruptedException e) {
e.printStackTrace();
}
sout("after waiting, thread1 is going on");
sout("thread1 is over");
}
}
}
class Thread2 extends Thread{
private void sout(String s){
System.out.println(s+" "+new SimpleDateFormat("HH:mm:ss:SS").format(new Date()));
}
@Override
public void run() {
sout("enter Thread2.run");
synchronized (TestSleepAndWait.class){//wait只能在同步代码块或者同步方法中使用
sout("Thread2 is going to notify");
TestSleepAndWait.class.notify(); 这里只能使用持有锁TestSleepAndWait.class
sout("thread2 is going to sleep 10ms");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sout("after sleeping, thread2 is going on");
sout("thread2 is over");
}
}
}
运行结果
enter Thread1.run 22:49:28:588
Thread1 is going to wait 22:49:28:588
enter Thread2.run 22:49:28:668
Thread2 is going to notify 22:49:28:668
thread2 is going to sleep 10ms 22:49:28:668
after sleeping, thread2 is going on 22:49:28:680
thread2 is over 22:49:28:680
after waiting, thread1 is going on 22:49:28:680
thread1 is over 22:49:28:680
分析:
可以看到,两个线程都使用TestSleepAndWait.class作为锁对象,
线程1首先获得执行权后,调用了锁对象的wait(),进而释放了锁和共享资源;
这时候线程2才获得执行权,而后进入了10ms的休眠期; 在10ms的休眠期间尽管线程2调用了锁对象的notify方法,线程1也没能获得执行权,这是因为线程2没有释放锁,线程1无法继续执行同步代码,直到线程2休眠结束,并执行完后续逻辑后,线程一才获得执行权。
线程1首先获得执行权后,调用了锁对象的wait(),进而释放了锁和共享资源;
这时候线程2才获得执行权,而后进入了10ms的休眠期; 在10ms的休眠期间尽管线程2调用了锁对象的notify方法,线程1也没能获得执行权,这是因为线程2没有释放锁,线程1无法继续执行同步代码,直到线程2休眠结束,并执行完后续逻辑后,线程一才获得执行权。