25°

Java 中的等待唤醒机制透彻讲解

线程的状态

首先了解一下什么是线程的状态,线程状态就是当线程被创建(new),并且启动(start)后,它不是一启动就进入了执行状态(run),也不是一直都处于执行状态。

这里说一下Java 的Thread类里面有一个State方法,这个方法里面涵盖了6种线程的状态,如下:

public enum State {
    // 尚未启动的线程的线程状态。
    NEW,
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 可运行线程的线程状态。</span>

RUNNABLE,

</span><span style="color: #008000;">//</span><span style="color: #008000;"> 线程的线程状态被阻塞,等待监视器锁定。</span>

BLOCKED,

</span><span style="color: #008000;">//</span><span style="color: #008000;"> 等待线程的线程状态。</span>

WAITING,

</span><span style="color: #008000;">//</span><span style="color: #008000;"> 具有指定等待时间的等待线程的线程状态。</span>

TIMED_WAITING,

</span><span style="color: #008000;">//</span><span style="color: #008000;"> 终止线程的线程状态。</span>

TERMINATED; }

 

导致这六种线程状态发生的条件

New -- 新建

线程刚被创建,不过还没有被启动(还没有调用start方法)

 

Runnable -- 可运行

处于可运行状态的线程正在Java虚拟机中执行,但是它可能正在等待来自操作系统(例如处理器)的其他资源。

 

Blocked -- 锁阻塞

当一个线程想获取一个对象锁,不过该对象锁被其它的线程持有时,该线程就会进入锁阻塞状态;当该线程持有锁的时候,该线程将会变成可运行的状态。

 

Waiting -- 无限等待

当一个线程在等待另一个线程执行一个(唤醒)动作时,该线程就会进入无限等待状态。进入这个状态后是不能自动唤醒的,要等待另一个线程调用notify()方法,或notifyall()方法才能够被唤醒。

 

Timed_Waiting -- 计时等待

类似于无限等待状态,有几个方法有超时参数,如:Thread.sleep、Object.wait方法。调用这些方法,进入计时等待状态。计时等待状态将会一直保持到超时期满或者接收到唤醒通知。

 

terminated--被终止

1、因为run方法的正常退出而死亡。
2、因为没有捕获的异常,终止了run方法而死亡。

 

等待唤醒案例切入

顾客要去饭店吃饭,自助下单,说明要吃什么,数量是多少。下完单以后,顾客就等待该饭店厨师做饭菜,也就是Waiting状态(无限等待状态)。

厨师收到下单信息,开始做饭菜,做好饭菜,把饭菜递到顾客桌面上,顾客看到饭菜已经来了(notify方法),就可以开吃了(等待唤醒机制)。

 

Java代码实现(线程之间的通信)

分析

创建一个顾客线程:下单,告知厨师要什么菜,菜的数量,调用wait方法,放弃CPU的执行,进入到无限等待状态(Waiting)

创建一个厨师线程:看到下单,花了3秒钟做饭菜,做好之后,调用notify方法,唤醒顾客吃饭了。

注意

顾客线程和厨师线程,必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。

同步使用的锁对象必须保证唯一。

只有锁对象才能够调用Object.wait方法和Object.notify方法。

代码

public class Demo01WaitNotify {
    public static void main(String[] args) {
        // 创建锁对象(要保证锁唯一)
        Object object = new Object();
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 创建一个顾客线程</span>
    <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread() {
        @Override
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。</span>
            <span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (object) {
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> 顾客下单</span>
                System.out.println("我要一个西虹市炒番茄,一个马铃薯炒土豆,两碗米饭"<span style="color: #000000;">);
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> 调用wait方法,放弃CPU的执行,进入到无限等待状态(Waiting)</span>
                <span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                    object.wait();
                } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
                    e.printStackTrace();
                }
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> 唤醒之后(饭菜上来后),吃饭!!!真香。</span>
                System.out.println("我就是饿死,从这里跳下去,也不会吃你们一口饭。。。真香!!!!"<span style="color: #000000;">);
            }
        }
    }.start();

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 创建一个厨师线程</span>
    <span style="color: #0000ff;">new</span><span style="color: #000000;"> Thread() {
        @Override
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 厨师收到下单请求,花三秒钟把饭菜做好</span>
            <span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                Thread.sleep(</span>3000<span style="color: #000000;">);
            } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
                e.printStackTrace();
            }
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。</span>
            <span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> (object) {
                System.out.println(</span>"我的饭菜三秒钟做好了,你食唔食哦?"<span style="color: #000000;">);
                </span><span style="color: #008000;">//</span><span style="color: #008000;"> 做好之后,调用notify方法,唤醒顾客吃饭了。</span>

object.notify(); } } }.start(); } }

控制台输出:
我要一个西虹市炒番茄,一个马铃薯炒土豆,两碗米饭
我的饭菜三秒钟做好了,你食唔食哦?
我就是饿死,从这里跳下去,也不会吃你们一口饭。。。真香!!!!

 

上面的代码,存在线程间的通信,那什么又是线程间的通信呢?简单的说,就是多个线程在处理同一个资源,但是处理的动作(线程的任务)却不同。如上,厨师线程做饭菜,顾客线程吃饭菜。那为什么要进行线程间的通信呢?多个线程并发执行的时候,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且希望它们有规律的执行的时候,那么多线程就之间就需要一些协调通信,来达到多线程共同操作一份数据。

 

对代码中通信的理解:

对又没有饭菜进行判断——

1、没有饭菜(False)。

2、顾客下单。

3、厨师做饭菜。

4、顾客线程等待。

5、厨师做好饭菜

6、修改饭菜的状态(True)

7、有饭菜,厨师线程提醒顾客线程吃饭菜。

8、厨师线程等待

9、吃完饭菜,修改饭菜的状态(False)

这就是顾客线程与厨师线程之间的通信。以此类推,其它Java程序中多线程的通信也是同样的道理。

本文转载自博客园,原文链接:https://www.cnblogs.com/liyihua/p/12216197.html

全部评论: 0

    我有话说: