线程方法
更新: 1/22/2026 字数: 0 字 时长: 0 分钟
Thread 类 API:
| 方法 | 说明 |
|---|---|
public void start() | 启动一个新线程,Java 虚拟机调用此线程的 run 方法 |
public void run() | 线程启动后调用该方法 |
public void setName(String name) | 给当前线程取名字 |
public void getName() | 获取当前线程的名字 线程存在默认名称:子线程是 Thread-索引,主线程是 main |
public static Thread currentThread() | 获取当前线程对象,代码在哪个线程中执行 |
public static void sleep(long time) | 让当前线程休眠多少毫秒再继续执行Thread.sleep(0):让操作系统立刻重新进行一次 CPU 竞争 |
public static native void yield() | 提示线程调度器让出当前线程对 CPU 的使用 |
public final int getPriority() | 返回此线程的优先级 |
public final void setPriority(int priority) | 更改此线程的优先级,常用 1 5 10 |
public void interrupt() | 中断这个线程,异常处理机制 |
public static boolean interrupted() | 判断当前线程是否被打断,清除打断标记 |
public boolean isInterrupted() | 判断当前线程是否被打断,不清除打断标记 |
public final void join() | 等待这个线程结束 |
public final void join(long n) | 等待线程运行结束,最多等待 n 毫秒,0 意味着永远等待 |
public final native boolean isAlive() | 线程是否存活(还没有运行完毕) |
public final void setDaemon(boolean on) | 将此线程标记为守护线程或用户线程 |
run、start
run:
- 称为线程体,包含了要执行的这个线程的内容,方法运行结束,此线程随即终止
- 直接调用
run是在主线程中执行了run,没有启动新的线程,需要顺序执行
start:
- 使用
start是启动新的线程,此线程处于就绪(可运行)状态,通过新的线程间接执行run中的代码
说明:Thread 类是 Java 中用于创建和管理线程的核心类,它封装了线程的生命周期和操作。
run() 方法中的异常不能抛出,只能 try/catch
- 因为父类中没有抛出任何异常,子类不能比父类抛出更多的异常
- 异常不能跨线程传播回
main()中,因此必须在本地进行处理
sleep、yield
sleep:
- 调用
sleep会让当前线程从Running进入Timed Waiting(阻塞)状态 sleep()方法的过程中,线程不会释放对象锁- 其它线程可以使用
interrupt方法打断正在睡眠的线程,这时sleep方法会抛出 InterruptedException - 睡眠结束后的线程未必会立刻得到执行,需要抢占 CPU
- 建议用 TimeUnit 的
sleep代替 Thread 的sleep来获得更好的可读性
yield:
- 调用
yield会让提示线程调度器让出当前线程对 CPU 的使用 - 具体的实现依赖于操作系统的任务调度器
- 会放弃 CPU 资源,锁资源不会释放
join
public final void join():等待这个线程结束
原理:调用者线程进入被 join 线程的 waitSet 等待,直到被 join 线程结束并发出通知。其内部机制可以类比为:
public final synchronized void join(long millis) throws InterruptedException {
// 调用者线程(如 main 线程)获取当前线程对象(t1)的锁,如果 t1 仍然存活,则进入 t1 的 waitSet 等待。
while (isAlive()) {
wait(0);
}
}join方法是被synchronized修饰的,本质上是一个对象锁,其内部的wait方法调用也是释放锁的,但是释放的是当前的线程对象锁,而不是外面的锁当调用某个线程(t1)的
join方法后,是调用者线程(例如 main 线程)暂停执行,等待 t1 线程完成。join方法并不会让 t1 线程抢占 CPU 资源或优先执行,t1 线程的调度依然由操作系统决定
线程同步:
join实现线程同步,因为会阻塞等待另一个线程的结束,才能继续向下运行- 需要外部共享变量来获取结果,不符合面向对象封装的思想(对于结果传递而言)
- 必须等待线程结束,不能配合线程池使用(因为线程池管理的是任务,而非直接的 Thread 对象)
- Future 实现(同步):
get()方法阻塞等待执行结果main线程接收结果get方法是让调用线程同步等待
public class Test {
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000); // t1 线程休眠 1 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
r = 10; // t1 线程将 r 设置为 10
});
t1.start();
t1.join();// main 线程会等待 t1 线程执行结束,因此会输出 10
System.out.println(r);
}
}interrupt
打断线程
public void interrupt():打断这个线程,异常处理机制
public static boolean interrupted():判断当前线程是否被打断,打断返回 true,清除打断标记,连续调用两次一定返回 false
public boolean isInterrupted():判断当前线程是否被打断,不清除打断标记
interrupt()方法的主要作用是设置线程的打断标记。它不会立即停止线程的执行,也不会强制线程进行上下文切换。线程需要通过isInterrupted()或interrupted()主动检查打断标记,或处理InterruptedException来响应打断请求。
sleep、wait、join 方法都会让线程进入阻塞状态,打断线程会清空打断状态(false)
javapublic static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("在 catch 块中,打断状态:" + Thread.currentThread().isInterrupted()); // 输出 false } }, "t1"); t1.start(); Thread.sleep(500); // main 线程等待 0.5 秒,确保 t1 进入 sleep t1.interrupt(); // 打断 t1 线程的 sleep System.out.println(" t1 线程的打断状态:" + t1.isInterrupted()); // 输出 false }打断正常运行的线程:不会清空打断状态(true)
javapublic static void main(String[] args) throws Exception { Thread t2 = new Thread(()->{ while(true) { Thread current = Thread.currentThread(); boolean interrupted = current.isInterrupted(); if(interrupted) { System.out.println("打断状态:" + interrupted); // 打断状态:true break; // 线程响应打断,退出循环 } } }, "t2"); t2.start(); Thread.sleep(500); // main 线程等待 0.5 秒 t2.interrupt(); // 打断 t2 线程 }
打断 park
park 作用类似 sleep,打断 park 线程,不会清空打断状态(true)
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
System.out.println("park...");
LockSupport.park();
System.out.println("unpark...");
System.out.println("打断状态:" + Thread.currentThread().isInterrupted()); // 打断状态:true
}, "t1");
t1.start();
Thread.sleep(2000); // 等待 t1 线程启动并进入 park 状态
t1.interrupt(); // 打断 t1 线程
}如果在调用 LockSupport.park() 之前,线程的打断标记已经是 true,那么 park() 方法会立即返回,不会阻塞。
public static void main(String[] args) {
Thread current = Thread.currentThread();
current.interrupt(); // 手动设置当前线程的打断标记为 true
System.out.println("第一次 park 前,打断状态:" + current.isInterrupted()); // true
LockSupport.park(); // 由于打断标记为 true,park 会立即返回,不会阻塞
System.out.println("第一次 unpark...");
System.out.println("第一次 unpark 后,打断状态:" + current.isInterrupted()); // 仍然是 true (park 不会清除打断标记)
// 注意:LockSupport.park() 不会清除打断标记,所以如果再次调用 park,它依然会立即返回。
// 如果需要清除打断标记,需要手动调用 Thread.interrupted()
current.interrupted(); // 清除打断标记,将打断状态设为 false
System.out.println("清除打断标记后,打断状态:" + current.isInterrupted()); // false
LockSupport.park(); // 这次会阻塞,因为打断标记已清除
System.out.println("第二次 unpark..."); // 这行代码通常不会被执行,除非有 unpark() 或再次 interrupt()
}终止模式
终止模式之两阶段终止模式:Two Phase Termination
目标:在一个线程 T1 中如何优雅终止线程 T2?优雅指的是给 T2 一个后置处理器
错误思想:
- 使用线程对象的 stop() 方法停止线程:stop 方法会真正杀死线程,如果这时线程锁住了共享资源,当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
- 使用 System.exit(int) 方法停止线程:目的仅是停止一个线程,但这种做法会让整个程序都停止
两阶段终止模式图示:

打断线程可能在任何时间,所以需要考虑在任何时刻被打断的处理方法:
public class Test {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination tpt = new TwoPhaseTermination();
tpt.start();
Thread.sleep(3500);
tpt.stop();
}
}
class TwoPhaseTermination {
private Thread monitor;
// 启动监控线程
public void start() {
monitor = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Thread thread = Thread.currentThread();
if (thread.isInterrupted()) { // 检查打断标记,决定是否进行后置处理并退出
System.out.println("后置处理");
break; // 退出循环,线程终止
}
try {
Thread.sleep(1000); // 睡眠
System.out.println("执行监控记录"); // 在此被打断不会异常
} catch (InterruptedException e) { // 在 sleep 期间被打断,会抛出 InterruptedException
e.printStackTrace();
// 捕获 InterruptedException 后,打断标记会被清除
// 为了让外层循环的 isInterrupted() 判断为 true 从而执行后置处理并退出,需要重新设置打断标记。
thread.interrupt();
}
}
}
});
monitor.start();
}
// 停止监控线程
public void stop() {
monitor.interrupt();
}
}daemon
public final void setDaemon(boolean on):如果是 true,将此线程标记为守护线程
线程启动前调用此方法:
Thread t = new Thread() {
@Override
public void run() {
System.out.println("running");
// 守护线程的特点是随着用户线程的结束而结束,
// 为了更好地观察这个特性,可以让守护线程执行一个耗时操作。
try {
Thread.sleep(5000); // 模拟守护线程长时间运行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程执行完毕"); // 这行可能不会被打印,取决于主线程何时结束
}
};
// 设置该线程为守护线程
t.setDaemon(true);
t.start();
System.out.println("主线程结束");用户线程:
- 平常创建的普通线程
- JVM 会等待所有用户线程执行完毕才会退出
守护线程:
- 服务于用户线程,只要所有非守护线程运行结束了,即使守护线程的代码没有执行完,JVM 也会强制结束所有守护线程,然后退出
- 守护线程是一种特殊类型的线程,它的存在不阻止 JVM 退出
说明:当运行的线程都是守护线程,JVM 将退出,因为普通线程执行完后,JVM 是守护线程,不会继续运行下去
常见的守护线程:
- 垃圾回收器线程就是一种守护线程
- Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求
不推荐
不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁:
public final void stop():强制停止线程运行废弃原因:方法粗暴,除非可能执行 finally 代码块以及释放 synchronized 外,线程将直接被终止,如果线程持有 JUC 的互斥锁可能导致锁来不及释放,造成其他线程永远等待的局面
public final void suspend():挂起(暂停)线程运行废弃原因:如果目标线程在暂停时对系统资源持有锁,则在目标线程恢复之前没有线程可以访问该资源,如果恢复目标线程的线程在调用 resume 之前会尝试访问此共享资源,则会导致死锁
public final void resume():恢复被suspend()挂起的线程运行
