复习
1.1线程是什么?
- 线程的定义与作用线程是轻量级的进程,它的出现是为了解决进程 "太重" 的问题 ------ 创建和销毁进程的开销较大,而线程的开销相对较小,能更高效地实现程序的并发执行。
1.2线程与进程的区别?
1.进程包含线程
2.进程是操作系统资源分配的基本单位
3.同一个进程中的多个线程之间,共用同一份资源(内存,文件)
1.3创建线程的方法第三种:内部匿名类
父类引用可以指向子类对象
            
            
              java
              
              
            
          
          Thread t = new Thread() {
};- new Thread() { ... }创建的是- Thread的一个匿名子类实例(匿名内部类本质是子类或接口实现类),它重写了- Thread的- run()方法,拥有自己的具体实现。
- Thread t声明了一个- Thread类型的变量- t,用于存储这个匿名子类的实例。由于匿名子类本身是- Thread的子类,因此可以安全地赋值给父类类型的变量。
            
            
              java
              
              
            
          
          package thread;
public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t;
        t =  new Thread(){
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("hello niming");
            }
        }
    };
    t.start();
        System.out.println("main______________________");
        while (true) {
            Thread.sleep(100);
            System.out.println("hello main");
        }
}
}注意:Thread.sleep 这是静态的方法,一定要注意
用于一般是一次性的,用完就不需要了,就可以使用匿名内部类
1.4创建线程的第四种:Runnable 加上匿名内部类
            
            
              java
              
              
            
          
          Runnable a = nwe Runnable() {
};接口本身不能被实例化,但匿名内部类会隐式创建一个实现了该接口的子类 ,并同时创建这个子类的实例。因此,new 接口名() { ... } 本质上是创建了接口的匿名实现类的实例
            
            
              java
              
              
            
          
          public class Demo4 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread t = new Thread(runnable);
        t.start();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}1.5创建线程的第五种方法:针对三和四的改进,Lambda表达式
            
            
              java
              
              
            
          
          // 使⽤lambda 表达式创建 Runnable ⼦类对象
 
Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
 Thread t4 = new Thread(() -> {
 System.out.println("使⽤匿名类创建 Thread ⼦类对象");
 });1.6多线程的优势
-增加运行速度 可以观察多线程在⼀些场合下是可以提高程序的整体运行效率的。
• 使用 System.nanoTime() 可以记录当前系统的纳秒级时间戳.
• se rial 串行的完成⼀系列运算. concurrency 使用两个线程并行的完成同样的运算.
            
            
              java
              
              
            
          
          public class ThreadAdvantage {
    // 多线程并不一定能提高速度,可以观察,count 不同,实际的运行效果也是不同的
    private static final long count = 10_0000_0000;
    public static void main(String[] args) throws InterruptedException {
        // 使用并发方式
        concurrency();
        // 使用串行方式
        serial();
    }
    private static void concurrency() throws InterruptedException {
        long begin = System.nanoTime();
        // 利用一个线程计算 a 的值
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (long i = 0; i < count; i++) {
                    a--;
                }
            }
        });
        thread.start();
        // 主线程内计算 b 的值
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        // 等待 thread 线程运行结束
        thread.join();
        // 统计耗时
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("并发:%.f 毫秒\n", ms);
    }
    private static void serial() {
        // 全部在主线程内计算 a、b 的值
        long begin = System.nanoTime();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a--;
        }
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
        }
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("串行:%.f 毫秒\n", ms);
    }
}并发:399.651856毫秒
串行:720.616911毫秒
2.Thread类及常见方法
Thread 类是JVM用来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的Thread对象与之关 联。 用我们上面的例子来看,每个执行流,也需要有⼀个对象来描述,类似下图所示,而Thread类的对象 就是用来描述⼀个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。

2.1 Thread的常见的构造方法

1.第一个构造方法Thread () 需要重写 run 方法
2.第二个方法就不需要重写 run 方法了
3.第四个第五个是给线程起名字

1.这几个名字就是他的线程,那为什么主线程(main)没有呢?
main方法执行完,程序就结束了(进程)
我们不结束main 方法不就行了
虽然虽然主线程结束了但是t1 ,t2,t3 线程没有结束,就会影响进程继续存在,这就是前台线程
JVM自带的线程,他们的存在不影响进程的结束,他们随着进程的就结束就结束了(因为其他的都执行完了,这个线程也没有存在的必要了),这种的就是后台进程
说白了就是我们程序员的可以控制的就是前台线程,不能控制结束的就是后端线程
2.1 Thread的几个常见属性

• ID是线程的唯一标识,不同线程不会重复
• 名称是各种调试工具用到
• 状态表示线程当前所处的⼀个情况,下面我们会进一步说明
• 优先级噶的线程理论上来说更容易被调度到
• 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有非后台线程结束后,才会结束运行。
• 是否存活,即简单的理解,为run方法是否运行结束了
• 线程的中断问题,下面我们进一步说明
            
            
              java
              
              
            
          
          public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还活着");
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
        });
        System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName() + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName() + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName() + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName() + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName() + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {}
        System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());
    }
}什么是后台进程?
这是关于 Java 守护线程(后台线程)的概念梳理:
- 方法与含义 :isDaemon()方法用于判断线程是否为守护线程(后台线程)。
- 概念等价:守护线程 == 后台线程;与之相对的是前台线程。
- 核心特点 :
- 前台线程:若程序中还有前台线程在运行,进程不会结束;
- 守护线程:是为前台线程服务的 "后台支持线程",当所有前台线程结束时,守护线程会被强制终止(即使自身任务未完成)。
- 典型例子:JVM 的垃圾回收线程就是守护线程,它会在前台线程都结束后自动停止。
 
注意:我们创建的线程默认是前台线程包括main主线程,可以通过setDaemon()方法来修改
2.2setDaemon() 方法的使用
            
            
              java
              
              
            
          
          package thread;
public class demo_setDaemon {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(true){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("test thread");
            }
        },"testFinished");
//要设置在start开始之前,设置为了后台进程
        t.setDaemon(true);
        t.start();
        for (int i = 0; i < 10000; i++) {
            Thread.sleep(10);
            System.out.println("888");
        }
    }
}运行结果:

- 
创建线程 t:线程t内部是一个无限循环,每秒打印一次"test thread"(实际通过sleep(1)控制频率,接近持续运行),并命名为"testFinished"。
- 
设置为守护线程 :通过 t.setDaemon(true)将t标记为守护线程,注意:此设置必须在start()之前调用,否则会抛出异常。
- 
主线程逻辑 :主线程( main线程)循环 10000 次,每次休眠 10 毫秒并打印"888",执行完毕后主线程结束。
运行结果与结论:
- 当主线程的循环执行完毕(打印完 10000 次 "888"后),主线程作为前台线程结束。
- 此时,程序中已没有其他前台线程运行,守护线程 t会被强制终止(即使其内部的无限循环未执行完),整个进程随之结束。
核心结论:
守护线程的生命周期依赖于前台线程:
- 只要有一个前台线程在运行,守护线程就会继续工作;
- 当所有前台线程都结束时,守护线程会被 JVM 自动终止,进程结束。
- IDEA 与 Java 进程的关系 :IDEA 本身是一个 Java 进程;在 IDEA 中运行 Java 代码时,会通过 IDEA 进程创建一个新的 Java 进程,这两个进程存在父子关系。
- 进程与线程的区别(父子关系维度) :进程之间存在父子关系;而线程之间不存在父子关系。
2.3isAlive() 的使用
Java 中 Thread 对象与系统实际线程的关系,核心要点如下:
- 
isAlive()方法 :用于判断系统中的线程是否处于 "存活" 状态(即线程是否已启动且未终止)。
- 
一一对应关系 :Java 代码中创建的 Thread对象,和系统底层的实际线程是一一对应的 (一个Thread对象对应一个系统线程)。
- 
生命周期差异 : Thread对象的生命周期(作为 Java 对象的存在时间)和系统线程的生命周期(实际执行任务的时间)并不相同 。javapackage thread; public class Demo8 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { for (int i = 0; i < 3; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t.start(); while (true) { System.out.println(t.isAlive()); Thread.sleep(1000); } } }执行结果 
- 
 
- 
线程执行逻辑与生命周期 :线程入口方法(如 run()方法)中的逻辑执行完毕后,系统中对应的线程会随之销毁(操作系统层面的线程终止)。
- 
代码示例说明:示例中线程内的循环执行 3 次,每次休眠 1000 毫秒(共 3 秒),逻辑执行完毕后,该线程在系统中就会被销毁。 
3秒后这时候这个线程已经执行完成了,但是这个线程还没有结束,所以就会出现这个情况
2.3.1多线程调度的随机性:
- 现象 :在多线程程序中,主线程判断子线程 t是否存活(通过isAlive()方法),子线程t运行时长约 3 秒,但主线程可能打印出 3 个或 4 个 "true"(表示子线程存活)。
- 原因 :这是由于线程调度的随机性 导致的。操作系统对线程的调度是 "随机" 且 "抢占式" 的,主线程第四次打印 "true" 的时机和子线程 t结束的时机可能存在重叠,因此结果不确定(可能 3 次,也可能 4 次)。
2.4 isInterrupted()
isInterrupted() 是 Java 中 Thread 类的一个实例方法,用于判断当前线程对象关联的线程 的中断标志位是否被设置。调用该方法后,不会清除中断标志位 (这是它和 Thread.interrupted() 方法的关键区别)
执行逻辑说明:
- 线程 t启动后,每秒钟打印 "线程运行中...",并通过!Thread.currentThread().isInterrupted()判断是否继续循环。
- 主线程休眠 3 秒后,调用 t.interrupt()设置线程t的中断标志位。
- 线程 t若在sleep期间被中断,会捕获InterruptedException,此时中断标志位会被自动清除;示例中手动调用Thread.currentThread().interrupt()重新设置标志位,确保循环条件!isInterrupted()为false,从而退出循环。
- 最后两次调用 t.isInterrupted()都会返回true,因为该方法不会清除中断标志位。
            
            
              java
              
              
            
          
          public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            // 循环判断线程是否被中断
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000); // 模拟耗时操作
                } catch (InterruptedException e) {
                    // 捕获到中断异常后,中断标志位会被清除
                    // 若要保持中断状态,需手动重新设置
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
            }
            System.out.println("线程因中断退出循环");
        });
        t.start();
        Thread.sleep(3000); // 主线程休眠3秒
        System.out.println("准备中断线程 t");
        t.interrupt(); // 设置线程 t 的中断标志位
        // 查看中断状态(不会清除标志位)
        System.out.println("线程 t 是否被中断:" + t.isInterrupted()); 
        System.out.println("线程 t 是否被中断:" + t.isInterrupted()); 
    }
}2.5启动一个线程-start()
之前我们已经看到了如何通过覆写 run 方法创建⼀个线程对象,但线程对象被创建出来并不意味着线 程就开始运行了。
这是对 Java 中 start() 方法的说明,可整理为以下要点:
- 方法作用:用于启动一个线程。
- 所属范畴:是 Java 标准库(由 JVM 提供)的方法。
- 底层原理:本质上是调用操作系统的 API 来创建并调度线程,使线程进入可运行状态。
• 覆写run方法是提供给线程要做的事情的指令清单
• 线程对象可以认为是把李四、王五叫过来了
• 而调⽤start()方法,就是喊⼀声:"行动起来!",线程才真正独立去执行了。
Java 中 Thread 对象使用规则的说明,可整理为以下要点:
- start()方法的限制 :每个- Thread对象只能调用一次- start()方法来启动线程,若重复调用会抛出异常。
- 对象的 "日抛" 特性 :每次需要创建新线程时,必须新建一个 Thread对象,不能重复利用已启动过的Thread对象来创建新线程。

调用start方法,才真的在操作系统的底层创建出⼀个线程.
2.6中断一个线程
李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我 们需要增加⼀些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停⽌转账,那张三 该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。 目前常见的有以下两种方式:
1.通过共享的标记来进行沟通
2.调用 interrupt() 方法来通知
示例-1:使用自定义的变量来作为标志位. 需要给标志位上加volatile关键字(这个关键字的功能后面介绍)
1. Lambda 中使用外部变量与 "变量捕获"
在 Lambda 表达式中,如果希望使用其定义作用域之外的变量,就会触发 "变量捕获" 语法。
- 不同编程语言(如 C++、Java、Python 等)对变量捕获的规则有所差异:
- 
例如在 C++ 中,捕获方式分为值捕获 ( [var])和引用捕获 ([&var]),值捕获会复制变量的值,引用捕获则是指向原变量的引用;
- 
在 Python 中,Lambda 对外部变量是闭包引用,会关联到变量的 "最新值",这可能引发一些执行时机相关的问题(如下文的回调场景)。 
- 
在 Java 中,Lambda 表达式对外部变量的捕获规则是仅允许捕获 "final 或 effectively final" 的变量。 
- 
final 变量 :显式用 final关键字修饰的变量,其值不可变。
- 
effectively final 变量 :未显式用 final修饰,但在程序执行过程中其值从未被修改过的变量,Java 编译器会将其视为 "effectively final"。
 
- 
2. Lambda 作为回调函数的执行时机
当 Lambda 作为回调函数时,其执行时机可能 "很晚"。
- 文中举例 "操作系统真正创建出线程之后才会执行":比如在多线程编程中,用 Lambda 注册线程回调,Lambda 定义时外部变量的状态,和线程真正执行时变量的状态可能不同。
- 典型问题:若 Lambda 捕获了循环变量(如 for 循环中的 i),且以引用方式捕获 (或 Python 式的闭包引用),当线程执行时,i可能已经是循环结束后的值,导致逻辑不符合预期。
 
Java Lambda 捕获引用类型变量规则:
- 引用本身不可修改:Lambda 捕获的引用类型变量,不能修改其指向(即不能让该引用指向其他对象)。
- 对象本体可修改:但引用指向的对象内部的属性、方法等是可以修改的。

拷贝意味着这样的变量就不适合进行修改
修改一方,另一方不会随之变化(本质上是两个变量)
这样的一边变,一边不变,会对程序员造成很大的困扰,Java的大佬就想办法设计了不让你修改
GC 是 "垃圾回收(Garbage Collection)" 的缩写,是 Java 等编程语言中自动管理内存的机制,主要作用如下:
- 自动回收内存:识别并回收不再被使用的对象所占用的内存,避免程序员手动管理内存时容易出现的内存泄漏、 double 释放等问题。
- 管理对象生命周期:像图中所说,对象本体由 GC 负责管理,不会随着方法结束而销毁,GC 会在合适的时机判断对象是否可回收,进而释放其内存。

            
            
              java
              
              
            
          
          public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        target.isQuit = true;
    }
}Thread.interrupt() 方法的作用说明:
- 
功能一:修改中断标志位(boolean 变量) t.interrupt()会修改线程t内部的中断标志位(一个 boolean 类型的变量),用于主动标记线程需要终止的意图。
- 
功能二:唤醒阻塞方法 除了设置中断标志位,它还能唤醒如 sleep这类会让线程进入阻塞状态的方法。例如,若线程因sleep处于阻塞,调用interrupt()会使其退出阻塞,并抛出InterruptedException,从而让线程有机会处理中断逻辑。
此时线程一般就会掀桌了 ,放置一个break 就不会了

示例-2:使用 Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位. Thread内部包含了⼀个boolean类型的变量作为线程是否被中断的标记

            
            
              java
              
              
            
          
          public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 两种方法均可以
            while (!Thread.interrupted()) {
            //while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ":别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ":有内鬼,终止交易!");
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ":啊!险些误了大事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ":让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ":老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
    }
}thread收到通知的方式有两种:
1.如果线程因为调用wait/join/sleep等方法而阻塞挂起,则以InterruptedException异常的形式通 知,清除中断标志 。当出现InterruptedException的时候,要不要结束线程取决于catch中代码的写法.可以选择忽 略这个异常,也可以跳出循环结束线程.
2.否则,只是内部的⼀个中断标志被设置,thread可以通过 Thread.currentThread().isInterrupted()判断指定线程的中断标志被设置,不清除中断标志 这种方式通知收到的更及时,即使线程正在sleep也可以马上收到
2.7等待⼀个线程-join()
有时,我们需要等待⼀个线程完成它的工作后,才能进行自己的下⼀步工作。例如,张三只有等李四 转账成功,才决定是否存钱,这时我们需要⼀个方法明确等待线程的结束。
            
            
              java
              
              
            
          
          public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName()
                            + ":我还在工作!");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ":我结束了!");
        };
        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始工作");
        thread1.start();
        thread1.join();
        System.out.println("李四工作结束了,让王五开始工作");
        thread2.start();
        thread2.join();
        System.out.println("王五工作结束了");
    }
}⼤家可以试试如果把两个join注释掉,现象会是怎么样的呢?
附录:

2.8 获取当前线程引用
这个方法我们已经非常熟悉了

            
            
              java
              
              
            
          
           public class ThreadDemo {
 public static void main(String[] args) {
 Thread thread = Thread.currentThread();
 System.out.println(thread.getName());
 }
}2.9 休眠当前线程
也是我们比较熟悉⼀组方法,有⼀点要记得,因为线程的调度是不可控的,所以,这个方法只能保证 实际休眠时间是大于等于参数设置的休眠时间的。

            
            
              java
              
              
            
          
           public class ThreadDemo {
 public static void main(String[] args) throws InterruptedException {
 System.out.println(System.currentTimeMillis());
 Thread.sleep(3 * 1000);
 System.out.println(System.currentTimeMillis());
 }
 }3. 线程的状态
3.1 观察线程的所有状态
线程的状态是⼀个枚举类型Thread.State
            
            
              java
              
              
            
          
           public class ThreadState {
 public static void main(String[] args) {
 for (Thread.State state : Thread.State.values()) {
 System.out.println(state);
 }
 }
 }• NEW:安排了工作,还未开始行动
• RUNNABLE:可工作的.又可以分成正在工作中和即将开始工作
. • BLOCKED:这几个都表水排队等着其他事情
• WAITING:这几个都表示排队等着其他事情
• TIMED_WAITING:这几个都表⽰排队等着其他事情
• TERMINATED:工作完成了
3.2 线程状态和状态转移的意义

大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思
还是我们之前的例子:
刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是NEW状态; 当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行 工作人员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的 调度; 当李四、王五因为⼀些事情需要去忙,例如需要填写信息、回家取证件、发呆⼀会等等时,进入BLOCKED 、 WATING 、 TIMED_WAITING 状态,至于这些状态的细分,我们以后再详解; 如果李四、王五已经忙完,为 TERMINATED 状态。 所以,之前我们学过的isAlive()方法,可以认为是处于不是NEW和TERMINATED的状态都是活着的
3.3 观察线程的状态和转移
观察1: 关注 NEW 、 RUNNABLE 、 1 2 3 4 TERMINATED 状态的转换
            
            
              java
              
              
            
          
          public class ThreadStateTransfer {
 public static void main(String[] args) throws InterruptedException {
 Thread t = new Thread(() -> {
 for (int i = 0; i < 1000_0000; i++) {
            }
        }, "李四");
        System.out.println(t.getName() + ": " + t.getState());;
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());;
        }
        System.out.println(t.getName() + ": " + t.getState());;
    }
 }观察2:关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
            
            
              java
              
              
            
          
          public static void main(String[] args) {
    final Object object = new Object();
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (object) {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }, "t1");
    t1.start();
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println("hehe");
            }
        }
    }, "t2");
    t2.start();
 }使⽤jconsole可以看到t1的状态是TIMED_WAITING,t2的状态是BLOCKED
修改上面的代码,把 t1中的sleep换成wait
            
            
              java
              
              
            
          
           public static void main(String[] args) {
    final Object object = new Object();
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    // [
修改这⾥就可以了
!!!!!] 
                    // Thread.sleep(1000);
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }, "t1");
        ...
 }使⽤jconsole可以看到t1的状态是WAITING
结论:
• BLOCKED表示等待获取锁,WAITING和TIMED_WAITING表示等待其他线程发来通知
. • TIMED_WAITING线程在等待唤醒,但设置了时限;WAITING线程在无线等待唤醒
