当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中, 有几种状态呢? 在API中 java.lang.Thread.State 这个枚举中给出了六种线程状态 初始(NEW):新创建了一个线程对象,但还没有调用start()方法 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running) 阻塞(BLOCKED):表示线程阻塞于锁 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断) 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回 终止(TERMINATED):表示该线程已经执行完毕

线程状态图.jpg
image.png
线程状态总详情.png
我们不需要去研究这几种状态的实现原理,我们只需知道在做线程操作中存在这样的状态。那我们怎么去理解这几 个状态呢,新建与被终止还是很容易理解的,我们就研究一下线程从Runnable(可运行)状态与非运行状态之间 的转换问题

Timed Waiting 线程状态图

Timed Waiting 线程状态图.png

BLOCKED 锁阻塞

BLOCKED(锁阻塞) .png

Waiting 无限等待

一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态
Waiting无限等待.png

  1. public class WaitingDemo {
  2. public static void main(String[] args) {
  3. Object lock = new Object();
  4. new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. synchronized (lock) {
  8. System.out.println("老板,来份牛排");
  9. try {
  10. lock.wait(); // 无限等待
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println("-------- 开吃 --------");
  15. }
  16. }
  17. }).start();
  18. new Thread(new Runnable() {
  19. @Override
  20. public void run() {
  21. synchronized (lock) {
  22. System.out.println("好咧,5号客人一份牛排");
  23. try {
  24. System.out.println("牛排制作中...");
  25. Thread.sleep(3000);
  26. // 调用notify方法,释放锁对象,让无限等待解除变成可运行
  27. lock.notify();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. }).start();
  34. }
  35. }

⚠️注意:在使用等待唤醒机制,应该满足

  • 同步使用的锁对象必须保证唯一
  • 只有锁对象才能调用wait和notify方法

java.lang.Object跟线程有关的几点**

方法 说明
void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待
void wait(long timeout) 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待
写参数与Thread.sleep(2000)用法相似
void notify() 唤醒在此对象监视器上等待的单个线程,会继续执行wait方法之后的代码
void notifyAll() 唤醒在此对象监视器上等待的所有线程

等待唤醒机制

线程间通信

多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。比如:

  • 线程A用来生成包子的
  • 线程B用来吃包子的

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

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

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

等待唤醒机制

等待唤醒是多个线程间的一种协作机制,谈到线程我们经常想到的是线程间的竞争(race),线程间也会有协作机制,wait/notify就是线程间的一种协作机制

  • wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时 的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象 上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
  • notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先 入座
  • notifyAll:则释放所通知对象的 wait set 上的全部线程

⚠️注意:

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

//包子资源类 class Bun { String pier; String xianer; boolean hasBun = false; // 有没有面包 }

// 吃货线程类: class Foodie extends Thread { private final Bun bun;

public Foodie(final String name, final Bun bun) { super(name); this.bun = bun; }

@Override public void run() { while (true) { synchronized (bun) { if (bun.hasBun == false) { try { // 没有包子等待 bun.wait(); } catch (final InterruptedException e) { e.printStackTrace(); } } try { System.out.println(this.getName() + “:吃货正在吃…”); sleep(3000); System.out.println(“—————— 吃完了 ——————\n”); } catch (InterruptedException e) { e.printStackTrace(); } bun.hasBun = false; bun.notify(); } } } }

class BunShop extends Thread { private final Bun bun; // 创建一个包子变量

public BunShop(final String name, final Bun bun) { super(name); this.bun = bun; }

@Override public void run() { while (true) { synchronized (bun) { if (bun.hasBun == true) { try { // 有包子不用再做 Thread.sleep(1000); System.out.println(“还有面包,面包师傅休息(进入wait())”); bun.wait(); } catch (final InterruptedException e) { e.printStackTrace(); } }

        // 没有的时候,开始做包子
        System.out.println("------包子铺开始做包子------");
        try {
           sleep(new Random().nextInt(5000) + 2000);
        } catch (final InterruptedException e) {
           e.printStackTrace();
        }

        final ArrayList<Object> list = new ArrayList<>();
        list.addAll(Arrays.asList("🐀", "🐂", "🐅", "🐇", "🐉", "🐍", "🐎", "🐏", "🐒", "🐓", "🐕", "🐖"));
        bun.pier = (String) list.get(new Random().nextInt(12));
        bun.xianer = "肉包子馅";
        bun.hasBun = true;
        System.out.println(this.getName() + ":" + bun.pier + bun.xianer + "包子做好了");
        bun.notify(); // 唤醒等待线程
     }
  }

} }

// 测试类 public class Demo { public static void main(final String[] args) { final Bun bun = new Bun(); final BunShop bunshop = new BunShop(“包子铺类线程”, bun); final Foodie foodie = new Foodie(“吃货类线程”, bun); bunshop.start(); foodie.start(); } } ```