复习
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线程在无线等待唤醒
