重难点:理解object和线程之间的关系以及wait、notify、notifyAll的调用者和调用时刻。
原理:
相关方法:
-
首先需要明确是:下面三个方法都是属于,synchronized锁的那个对象的,因此不管是在哪里出现这几个方法,都应该是那个对象来调用这三个方法,对象是调用者,线程不是。
-
其次就是Monitor和对象之间的关系,java对象的对象头里面的Mark Work指向Monitor(Java环境看不到,属于操作系统),每个对象关联一个Monitor。
-
obj.wait()让进入object监视器(object指向的Monitor)的线程拥有者到waitSet等待。
-
wait常用的方法是一个带参数一个不带参数,不带参数的底层是使用带参数的实现的,参数传的是0。wait()和wait(0)意思相同,都为一直等待。
-
obj .notify()在object指向的Monitor上正在waitSet等待的线程中随机挑一个唤醒,然后进入EntryList竞争。
-
obj.notifyAll() 让object指向的Monitor上正在waitSet等待的线程全部唤醒,然后进入EntryList竞争。
-
wait()和notify()的基本使用格式:
synchronized(obj){
while(条件不成立){
obj.wait()
}
//干活
}
synchronized(lock){
lock.notifyAll();
}
注意:
-
它们都是线程之间进行协作的手段,都属于Object对象的方法。必须获得此对象的锁,才能调用这几个方法。
-
如果没有获取到对象锁,直接调用上面三个方法,会抛出异常(java. lang .IllegalMonitorStateException)
注意中第一点的演示,结合原理中的图理解。
@Slf4j
public class Thread1 {
private static int current;
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t2 = new Thread(()->{
log.debug("线程2开始");
//获取object对象锁
synchronized (object){
log.debug("线2程进入等待");
try {
//让线程t2等待,进入Monitor的waitSet等待,状态变为WAITING状态,并且释放对象锁
object.wait(0); //这里的wiat(0)和wait()方法一样效果。
} catch (InterruptedException e) {
e.printStackTrace();
}
current = 100;
}
//当线程2被唤醒之后,执行下面的代码
log.debug("线程2被唤醒了");
log.debug("current:"+current);
log.debug("线程2返回");
},"t2");
Thread t1 = new Thread(()->{
log.debug("线程1开始");
synchronized (object){
log.debug("线程1获取到锁");
current = 101;
}
log.debug("current:"+current);
log.debug("线程1返回");
},"t1");
t2.start();
t1.start();
Thread.sleep(3000);
synchronized (object){
//只有获取到对象锁之后,才能使用对象的notifyAll方法
object.notifyAll();
}
}
}
最后看看,sleep(long n)和 wait(long n)的区别
-
sleep是Thread的方法,而wait是Object的方法。
-
sleep不需要强制和synchronized配合使用,但wait需要和synchronized一起用。
-
sleep在睡眠的同时,不会释放对象锁的,但wait在等待的时候会释放对象锁。
-
相同点是:调用它们之后,线程的状态是一样的,都是TIMED_WAITING。