Wait notify
分析:
T1获得了锁,正在干活,调用wait方法,T1到休息室(Waitset)并释放锁
其他线程获得锁执行。T2获得锁,调用notify通知T1干活,T1就到EntryList(blocked)继续竞争锁
调用wait方法,就进入了waitset,变为waiting状态
blocked与waiting都是阻塞,不占用cpu时间片
分析:
API介绍:
obj.wait() 让进入obj监视器的线程到waitset等待(调用wait(0):无限制等待)
obj.wait(long timeout) :等待timeout ms,如果没有被唤醒,就不等待了,就继续执行该线程剩余代码
obj.notify() 在obj上正在等待的线程中挑一个唤醒
obj.notifyAll() 在obj上正在等待的线程全部唤醒
注意:必须获得obj对象的锁,才能调用这几个方法
线程1 2 都调用wait方法,进入waiting状态
并释放锁,main获得锁可以叫醒线程1 2
notify:随机叫醒一个
notifyAll:全部叫醒
public class WaitNotify {
final static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
//线程t1,
new Thread(()->{
synchronized (obj){
System.out.println(Thread.currentThread().getName()+"执行...");
try {
obj.wait();//让线程在obj上一直等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行其他代码");
}
},"t1").start();
//线程t2
new Thread(()->{
synchronized (obj){
System.out.println(Thread.currentThread().getName()+"执行...");
try {
obj.wait();//t2获得锁,进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行其他代码");
}
},"t2").start();
Thread.sleep(2000);
System.out.println("主线程唤醒其他线程");
synchronized (obj){
//主线程获得锁,调用唤醒等待状态的线程的方法
obj.notify(); //唤醒obj上的一个线程
//obj.notifyAll();//唤醒obj上所有的等待线程
}
}
}
sleep(long n) wait(long n)区别
sleep() 是Thread的静态方法,wait()是Object的方法
sleep 不强制和synchronized配合使用。wait强制和synchronized一起用
sleep在睡眠的时候不释放锁,wait释放
例子:
//wait的时候,T释放了锁,所以main线程马上获得了锁
//sleep的时候,T没有释放锁,main线程不能获得锁
public class SleepWait {
private final static Object obj=new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (obj){
try {
// Thread.sleep(20000);
obj.wait(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(1000);
//wait的时候,T释放了锁,所以main线程马上获得了锁
//sleep的时候,T没有释放锁,main线程不能获得锁
synchronized (obj){
System.out.println("主线程获得锁");
}
}
}
wait notify的正确使用姿势
public class WaitNotify01 {
private final static Object ob = new Object();
private static Boolean hasc=false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (ob){
System.out.println("有烟吗");
if(!hasc){
System.out.println("没有烟,休息一下");
try {
ob.wait();//释放锁,其他线程可以使用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有烟吗");
if(hasc){
System.out.println("小南开始干活");
}
}
}).start();
for (int i = 0; i < 5; i++) {
new Thread(()->{
synchronized (ob){ //小南休息的时候释放了锁,可以正常干活
System.out.println("其他人开始干活");
}
}).start();
}
Thread.sleep(1000);
new Thread(()->{
synchronized (ob){
System.out.println("送来了");
hasc=true;
ob.notify();//叫醒小南
}
}).start();
}
}
问题:万一waitset中 不止小南一个,怎么办?
如果错误唤醒,就是虚假唤醒
比如:
public class WaitNotify02 {
private final static Object ob = new Object();
private static Boolean hasc=false;
private static Boolean hasw =false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (ob){
System.out.println("有烟吗");
if(!hasc){
System.out.println("没有烟,休息一下");
try {
ob.wait();//释放锁,其他线程可以使用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有烟吗");
if(hasc){
System.out.println("有烟了小南开始干活");
}else {
System.out.println("没有烟,为什么叫我?");
}
}
}).start();
new Thread(()->{
synchronized (ob){
System.out.println("有外卖吗?");
if(!hasw){
System.out.println("没有外卖,休息一下");
try {
ob.wait();//释放锁,其他线程可以使用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有外卖吗");
if(hasw){
System.out.println("有外卖小女开始干活");
}else {
System.out.println("没有外卖,为什么叫我");
}
}
}).start();
Thread.sleep(1000);
new Thread(()->{
synchronized (ob){
System.out.println("外卖到了。。。");
hasw=true;
ob.notify();//叫醒小女
}
}).start();
}
}
此时随机叫醒一个,叫醒了小南,但是我们想要叫醒小女
如果调用notifyAll:则全部会唤醒,小南小女全部被唤醒。但是不需要叫醒小南
改进:
可以用while替换if 就可以解决这个问题
public class WaitNotify03 {
private final static Object ob = new Object();
private static Boolean hasc=false;
private static Boolean hasw =false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (ob){
System.out.println("有烟吗");
while (!hasc){
System.out.println("没有烟,休息一下");
try {
ob.wait();//释放锁,其他线程可以使用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有烟吗");
if(hasc){
System.out.println("有烟了小南开始干活");
}else {
System.out.println("没有烟,为什么叫我?");
}
}
}).start();
new Thread(()->{
synchronized (ob){
System.out.println("有外卖吗?");
while (!hasw){
System.out.println("没有外卖,休息一下");
try {
ob.wait();//释放锁,其他线程可以使用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有外卖吗");
if(hasw){
System.out.println("有外卖小女开始干活");
}else {
System.out.println("没有外卖,为什么叫我");
}
}
}).start();
Thread.sleep(1000);
new Thread(()->{
synchronized (ob){
System.out.println("外卖到了。。。");
hasw=true;
ob.notifyAll();//叫醒小女
}
}).start();
}
}
总结正确使用方式:
synchronized(lock){
while(条件不成立){
lock.wait();
}
//干活
}
//另一个线程
synchronized(lock){
lock.notifyAll();
}