Java 线程wait()之后一定要notify()才能唤醒吗?

研究这个问题之前先要搞清楚线程有哪些状态?

1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked)  : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead)    : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

清楚了线程的状态之后,我们要搞懂wait()方法是谁来调用,执行wait()方法后,哪个线程进入等待的队列?

可参考java中wait()与锁的关系

接下来进入正题,Java 线程wait()之后一定要notify()才能唤醒吗?

我们来看一段代码:

class Person{
    private String name;
 
}
public class ThreadA
{
    //public static Person person=new Person();
    public static void main(String[] args)
    {
        ThreadB b = new ThreadB();
        ThreadC c = new ThreadC();
        c.setName("c线程");
        b.setName("b线程");
        c.start();
        System.out.println(Thread.currentThread().getName()+"is start....");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        synchronized(c)
        {
            try
            {
                System.out.println(Thread.currentThread().getName()+" synchronized method is beginning");
                System.out.println(c.isAlive());
                Thread.sleep(1000);
                System.out.println(c.isAlive());
                System.out.println(Thread.currentThread().getName()+ " is waiting");
                c.wait();
                System.out.println("now back to "+
                        Thread.currentThread().getName());
                b.start();
            }
            catch(InterruptedException e)
            {
            }
        }
    }
}
class ThreadB extends Thread
{
    int total;
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+" is running..");
        synchronized(this)
        {
 
            for(int i=0;i<10;i++)
            {
                total +=i;
            }
            System.out.println("total is "+total);
        }
    }
}
class ThreadC extends Thread
{
    //private Person person;
    int sum=1;
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+"is running..");
        synchronized(this)
        {
            System.out.println(Thread.currentThread().getName()+" synchronized method is beginning");
            for(int i=1;i<10;i++)
            {
                sum *=i;
            }
            System.out.println(Thread.currentThread().getName()+" synchronized method is ending");
//            notify();
        }
        for (int i=0;i<50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
        System.out.println("c线程运行结束");
    }
}


运行结果:

mainis start....
c线程is running..
c线程 synchronized method is beginning
c线程 synchronized method is ending
0
1
2
3
4
5
6
7
8
main synchronized method is beginning
true
9
10
11
12
13
14
15
16
17
true
main is waiting
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
c线程运行结束
now back to main
b线程 is running..
total is 45


上面的代码运行结果结合文章开头的线程状态是这样的:C线程和main线程先都由New->Runnable->Running,然后C线程先拿到了c同步对象锁,所以可以优先于main线程进入synchronized同步块,仍然保持Running状态,而main线程由Running->同步阻塞(锁定Blocked)状态。C线程执行完synchronized同步块后,立即释放c同步对象锁。此时main线程获得锁,锁定Blocked->Runnable->Running,进入synchronized同步块。这时候,C线程还在执行run()方法,main线程执行到c.wait()时,又从Running->等待Blocked,同时释放c同步对象锁。关键时候来了,C线程执行完run()方法后,C线程正常结束,以线程对象c运行的wait()线程(这里也就是main线程)退出等待队列,相当于唤醒,notify()/notifyAll(),所以main线程从等待Blocked->锁定Blocked,再获取同步对象锁,锁定Blocked->Runnable->Running。经过验证,这里的唤醒相当于notifyAll(),这里代码我就不贴了,有兴趣可以去验证一下。

所以,倘若是main线程先拿到c同步对象锁,运行结果也是会正常退出。但是如果我把C线程循环Thread.sleep(100)注释掉,结果可能就不一样了:

class ThreadC extends Thread
{
    //private Person person;
    int sum=1;
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+"is running..");
        synchronized(this)
        {
            System.out.println(Thread.currentThread().getName()+" synchronized method is beginning");
            for(int i=1;i<10;i++)
            {
                sum *=i;
            }
            System.out.println(Thread.currentThread().getName()+" synchronized method is ending");
            notify();
        }
/*        for (int i=0;i<50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }*/
        System.out.println("c线程运行结束");
    }
}


运行结果:

mainis start....
c线程is running..
c线程 synchronized method is beginning
c线程 synchronized method is ending
c线程运行结束
main synchronized method is beginning
false
false
main is waiting


因为main线程的c.wait()方法是在C线程正常结束后执行的,C线程正常结束时并没有可以唤醒的以c线程对象wait()的线程,等待队列是空的。所以之后当main线程执行的c.wait()方法进入到等待队列中,已经没有谁去唤醒它了,所以main线程将会一直处于等待状态,程序一直运行,无法正常退出。

 

参考博客:

https://blog.csdn.net/nmyangym/article/details/7850882#commentsedit

https://blog.csdn.net/Deronn/article/details/80450959

https://www.cnblogs.com/happy-coder/p/6587092.html

https://blog.csdn.net/u011784767/article/details/51426449

https://www.cnblogs.com/kevin-yuan/p/4112434.html
--------------------- 
作者:零怀念 
来源:CSDN 
原文:https://blog.csdn.net/linghuainian/article/details/81252966 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值