想要后执行的逻辑等待先执行的逻辑先跑,跑完后再继续执行。我们虽然无法直接干预调度器的顺序,但是可以让后执行的逻辑等待,等待到先执行的逻辑跑完了,通知当前线程,让他继续执行。方法:
1.join()方法:
join是等另一个线程彻底执行完,才继续执行。
2.wait()方法:
wait也是等,是等到另一个线程执行到notify后,才继续执行(不需要另一个线程执行完)
wait(等待)和notify(通知):
当多个线程竞争一把锁的时候,获取到锁的线程如果释放了,其他是哪个线程拿到锁?这是不确定的(cpu的随机调度)
操作系统的调度是随机的,其他线程属于在锁上阻塞等待,是阻塞状态,当前释放这把锁的线程,是就绪状态,有很大概率再次拿到这把锁
当拿到锁的线程,发现要执行的任务,时机还不是很成熟的时候,就使用wait阻塞等待。
wait和notify都是Object类的方法:
java中,任意的对象都提供了wait和notify方法
在java库中,任意产生阻塞的方法,都会抛出这个异常,意味着随时都可能被interrupt方法唤醒。
wait()方法:
object.wait();
第一步:释放object对象对应的锁(释放锁的前提,object对象应处于加锁状态,才能释放锁)
1.代码进入到wait,先释放锁,并且阻塞等待。
2.如果其他的线程做完了必要的工作,调用notify()唤醒wait()这个线程,wait就会解除阻塞,重新获取锁,继续执行并返回。
synchronized(object){
//加锁
object.wait();//释放锁
//加锁
}
要求synchronized加锁对象和wait的对象要一致。
notify()方法:
locker.notify():
第一步:同样需要先拿到锁,再进行notify(java中给出的限制)
synchronized(locker){
locker.wait();
}
synchronized(locker){
locker.notify();
}
这四处的对象都应该是同一个,才能生效的,这个相同的对象,是这两线程沟通的桥梁。
如果是两个不同的对象,则没有任何相互影响和作用。
注意:
1.wait操作必须搭配锁来进行,因为wait会先释放锁。
2.notify操作,原则上说,不涉及到加锁和解锁的操作,在java中,强制要求notify和synchronized搭配使用。
3.notify的执行一定要在wait之后,因为在之前,如果notify执行完了,但wait会一直阻塞等待。
4.如果有多个线程在同一个对象上进行wait,当notify执行完后,只会随机唤醒其中一个线程。
5.notifyAll方法可以一次性唤醒所有相同的对象的wait,但也要在wait后执行才行,注意这里同时唤醒了多个线程,因为被唤醒的线程都是对同一个对象加锁,所以在唤醒完后,只有一个线程能够加锁成功,其他线程只有等待这个线程执行完释放了锁后,再继续加锁执行。
wait()方法和sleep()方法:
wait方法提供了“死等”和“超时间”版本,wait可以用notify提前唤醒。
sleep()也有等待时间,sleep可以用interrupt提前唤醒(看起来的提前唤醒,其实作用是通知线程终止)。
wait()方法和sleep()方法的区别:
1.wait必须搭配锁,才能用wait(sleep不需要)
2.如果都是在synchronized内部使用,wait会释放锁,但sleep不会释放锁。
synchronized(locker){
Thread.sleep(1000);
}
此时sleep相当于抱着锁睡,其他线程这时不能获取到该锁。
而wait会释放锁,它会释放它所持有的锁,并进入等待状态,直到其他线程发出notify(通知)并唤醒它。这样可以实现线程之间的协调和同步。
注意:"wait"和"sleep"的使用场景不同,功能也不同。"wait"通常用于线程之间的通信和同步,而"sleep"用于线程的暂停和时间控制。