Java多线程Part3:线程(Thread)的状态和等待唤醒机制

一、线程状态概述

当线程被创建并启动以后 它既不是一启动就进入了执行状态 也不是一直处于执行状态 在线程的生命周期中有很多种状态:

New(新建状态) 线程刚被创建 但是并未启动 还没调用start方法
Runnable(可运行状态)线程可以在java虚拟机中运行的状态 可能正在运行自己的代码 也可能没有
Blocked(锁阻塞状态) 当一个线程试图获取一个对象锁 而该对象锁被其他的线程持有 则该线程进入Blocked状态
当该线程持有锁时 该线程将变成Runnable状态
Waiting(无限等待状态) 一个线程在等待另一个线程执行一个(唤醒)动作时 该线程进入Waiting状态
进入这个状态后是不能自动唤醒的 必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
TimedWaiting(计时等待状态) 同waiting状态 有几个方法有超时参数 调用他们将进入Timed Waiting状态
这一状态将一直保持到超时期满或者接收到唤醒通知 带有超时参数的常用方法有Thread.sleep()和Object.wait()
Teminated(终止状态) 因为run方法正常退出而死亡 或者因为没有捕获的异常终止了run方法而死亡
示意图

小案例:以包子铺为例

★ 分析:

【壹】、TimedWaiting(计时等待状态)
一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态

当调用了sleep方法之后 当前执行的线程就进入到“休眠状态” 其实就是所谓的Timed Waiting(计时等待)

1. 进入Timed Waiting状态的一种常见情形是调用sleep()方法 单独的线程也可以调用 不一定非要有协作关系
2. 为了让其他线程有机会执行 可以将Thread.sleep()的调用放线程run()之内 这样才能保证该线程执行过程中会睡眠
3. sleep与锁无关 线程睡眠到期自动苏醒 并返回到Runnable(可运行)状态
【贰】、Blocked(锁阻塞状态)
一个正在阻塞等待一个监视器锁对象的线程处于这一状态。

这个状态非常好理解 比如线程A与线程B代码中使用同一个锁 如果线程A获
取到锁 线程A进入到Runnable状态 那么线程B就进入到Blocked锁阻塞状态
【叁】、Waiting(无限等待状态)
一个正在无限期等待另一个线程执行一个特别的唤醒动作的线程处于这一状态
线程之间的通信:
比如说 有一个包子店 一个顾客要来买包子
顾客和老板说明买的包子的种类和数量
然后等着老板做包子(此时调用wait()方法) 进入Waiting状态 无限等待 因为不知道老板要做多久的包子

然后老板做好了包子 唤醒顾客(此时调用notify()方法)
告诉顾客包子做好了 可以吃了

★ 代码实现

public class WaitAndNotify {
    public static void main(String[] args) {
        // 创建锁对象 保证唯一
        Object object=new Object();
        //创建顾客线程
        new Thread()
        {
            @Override
            public void run() {
                // 使用同步技术 保证等待和唤醒只能有一个在执行
                synchronized (object)
                {
                    System.out.println("老板 我要10个肉包");
                    try {
                        //等待
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    // 唤醒之后继续执行的代码
                    System.out.println("老板您家的包子真好吃");
                }
            }
        }.start();

        //创建包子铺老板线程
        new Thread()
        {
            @Override
            public void run() {
                try {
                    // 花5秒做包子
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 使用同步技术 保证等待和唤醒只能有一个在执行
                synchronized (object)
                {
                    // 唤醒顾客
                    object.notify();
                    System.out.println("包子做好了");
                }
            }
        }.start();
    }
}

带参wait()和notify()的功能
进入到TimeWaiting(计时等待状态)

1、使用sleep(Long m)方法 在毫秒值结束之后 线程睡醒 进入Runnable或Blocked状态
2、使用wait(Long m)方法 如果在毫秒值结束之后还没有被notify()唤醒 就会自动醒来 进入Runnable或Blocked状态

wait()和sleep()其实是一样的

二、线程通信

概念:多个线程在处理同一个资源 但是处理的动作(即线程的任务)各不相同

例:线程A用来生成包子 线程B用来吃包子 包子可以理解为同一个资源
线程A与线程B处理的动作一个是生产一个是消费 那么线程A与线程B之间就存在线程通信问题

为什么要处理线程间通信问题?

多个线程并发执行时 在默认情况下CPU是随机切换线程的 当我们需要多个线程来共同完成一件任务 并且我们希望他们有规律地执行
那么多线程之间需要一些协调通信 以此来帮我们达到多线程共同操作一份数据

如何保证线程间通信有效利用资源?

多个线程在处理同一个资源并且任务不同时 需要线程通信来帮助解决线程之间对同一个变量的使用或操作
就是多个线程在操作同一份数据时避免对同一共享变量的争夺
也就是需要通过一定的手段使各个线程能有效利用资源 而这种手段即—— 等待唤醒机制

三、等待唤醒机制

1、概述

什么是等待唤醒机制?
这是多个线程间的一种协作机制
谈到线程我们经常想到的是线程间的竞争(race) 比如去争夺锁 但这并不是全部 线程间也会有协作机制 就好比在公司里你和你的同事们 你们之间可能会存在在晋升时的竞争 但更多时候你们更多是一起合作以完成某些任务

在一个线程进行了规定操作后就进入了等待状态(wait) 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify) 在有多个线程进行等待时 如果需要 可以使用 notifyAll()来唤醒所有的等待线程

wait和notify 就是线程间的一种协作机制

注:
①. wait方法与notify方法必须要由同一个锁对象调用
因为对应的锁对象是通过notify唤醒使用同一个锁对象调用的wait方法后的线程
②. wait方法与notify方法是属于Object类的方法的
因为锁对象可以是任意对象 而任意对象的所属类都是继承了Object类的
③. wait方法与notify方法必须要在同步代码块或者是同步函数中使用
因为必须要通过锁对象调用这2个方法


我们再来以包子铺为例:

2、分析

资源类:包子
包子的属性:是否有包子:true/false

生产者类:包子铺
该类为线程类 可以继承Thread
设置线程任务(run):生产包子
对包子的状态进行判断:

  • true:有包子 调用wait()方法进入等待状态
  • false:没有包子 生产包子 生产好了包子修改状态为true 然后唤醒消费者线程来吃包子

消费者类:食客
该类为线程类 可以继承Thread
设置线程任务(run):吃包子
对包子的状态进行判断:

  • true:有包子 吃包子 吃完修改状态为false 唤醒生产者线程去生产包子
  • false:没有包子 调用wait()方法进入等待状态

3、代码实现

包子类:

public class BaoZi {
	// 包子是否存在 默认不存在
    boolean flag=false;
}

生产者类(包子铺):

public class BaoZiShop extends Thread{
    private BaoZi baoZi;

	// 构造方法接收包子
    public BaoZiPu(BaoZi baoZi) {
        this.baoZi = baoZi;
    }

    @Override
    public void run() {
        synchronized (baoZi)
        {
            // 当前若有包子 则等待
            if (baoZi.flag)
            {
                try {
                    baoZi.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 下方代码是被唤醒之后才会执行
            System.out.println("正在生产...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            baoZi.flag=true;
            // 唤醒消费者线程
            baoZi.notify();
            System.out.println("包子已经做好了");
        }
    }
}

消费者类(食客):

public class Customer extends Thread{
    private BaoZi baoZi;

	// 构造方法接收包子
    public XiaoFeiZhe(BaoZi baoZi) {
        this.baoZi = baoZi;
    }

    @Override
    public void run() {
        synchronized (baoZi)
        {
            // 当前若没包子 则等待
            if (!baoZi.flag)
            {
                try {
                    baoZi.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 下方代码是被唤醒之后才会执行
            System.out.println("包子真好吃");
            baoZi.flag=false;
            // 唤醒包子铺线程
            baoZi.notify();
            System.out.println("吃好了 再给俺做个包子");
        }
    }
}

测试类:

public class Demo {
    public static void main(String[] args) {
        BaoZi baoZi=new BaoZi();

        new BaoZiShop(baoZi).start();

        new Customer(baoZi).start();
    }
}

发布了56 篇原创文章 · 获赞 0 · 访问量 1176

猜你喜欢

转载自blog.csdn.net/Piconjo/article/details/104644274