本文将介绍Java常用线程调度方法及实现原理,包括sleep、wait¬ify、join、park&unpark。
线程方法
方法 | 说明 |
---|---|
start() | 用于启动线程,让线程进入就绪状态 ; RUNNABLE 多次调用抛 IllegalThreadStateException 异常 |
run() | 线程运行时会调用 run() 方法执行具体逻辑 |
join() | 当前线程由运行状态转为等待状态 |
getId() | 获取线程ID |
getName()/setName() | 获取/设置线程名 |
getPriority()/setPriority() | 获取/设置优先级,优先级 1~10,默认 5,优先级仅起提示作用 |
getState() | 获取线程状态 |
interrupt() | 打断线程 |
isInterrupted() | 判断是否被打断,不会清除打断标记 |
static interrupted() | 判断是否被打断,会清除打断标记 |
isAlive() | 是否存活,即是否未运行完毕 |
static currentThread() | 获取当前正在运行的线程 |
static sleep() | 线程由运行状态转为等待状态; TimeUnit 的 sleep() 比 Thread 的 sleep() 更具可读性 |
static yield() | 线程由运行状态转为就绪状态 |
对于 interrupt()
-
处于等待状态(sleep、wait、join)的线程被 interrupt() 后 ,会抛异常 InterruptedException,打断标记为false(被清除);
-
处于等待状态(park)的线程被 interrupt() 后,打断标记为 true。park() 仅对打断标记为 false 的线程生效;
-
处于运行状态的线程被 interrupt() 后,线程仍正常执行,打断标记为 true
Thread - sleep
正在运行的线程主动调用 Thread.sleep(long) 方法,由运行状态转为等待状态。
public class Thread implements Runnable {
public static void sleep(long millis) throws InterruptedException {
if (millis < 0) {...}
long nanos = MILLISECONDS.toNanos(millis);
ThreadSleepEvent event = beforeSleep(nanos);
try {
if (currentThread() instanceof VirtualThread vthread) {
// 虚拟线程
vthread.sleepNanos(nanos);
} else {
// 最终调用本地方法 sleep0()
sleep0(nanos);
}
} finally {
afterSleep(event);
}
}
private static native void sleep0 (long nanos) throws InterruptedException;
}
object - wait & notify
正在运行的线程发现条件不满足时,主动调用 obj.wait(),进入 obj 对象对应的 Monitor 对象的 WaitSet 等待,状态转为 WAITING。
当其他线程调用 notify()/notifyAll() 方法时,会唤醒 WaitSet 中的一个/全部线程,使其进入 EntryList 参与锁的竞争。
public class Object {
public final void wait(long timeoutMillis) throws InterruptedException {
long comp = Blocker.begin();
try {
// 最终调用本地方法 wait0()
wait0(timeoutMillis);
} catch (InterruptedException e) {...} finally {...}
}
private final native void wait0(long timeoutMillis) throws InterruptedException;
}
thread - join
join() 体现的是保护性暂停模式(Guarded Suspension),join() 通过 wait() 实现等待。
保护性暂停模式:线程执行任务时,若不满足条件则转为等待状态(保护、暂停),直到满足条件。为防止虚假唤醒,线程每次被唤醒需要再次检查条件是否满足。
public class Thread implements Runnable {
// join(long) 的需要满足的条件就是超过等待时间或者被等待线程结束
public final void join(long millis) throws InterruptedException {
if (millis < 0){...};
if (this instanceof VirtualThread vthread) {..}
// 核心代码
synchronized (this) {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
// 等待
wait(delay);
} while (
// 被等待线程存活
isAlive() &&
// 等待时间未超过限制
(
// 计算当前线程继续等待时间
delay = millis -
NANOSECONDS.toMillis(
System.nanoTime() - startTime
)
) > 0
);
}
} else {
// 无时限等待,直到被等待线程结束
while (isAlive()) {
wait(0);
}
}
}
}
}
LockSupport - park & unpark
每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex 。
调用 LockSupport.park(),检查 _counter,若为 0,则进入 _cond 条件变量等待,若为 1,则获得互斥锁 _mutex ,将 _counter 置为0。
调用 LockSupport.unpark(),检查 _counter,将 _counter 置为1,唤醒条件变量 _cond 中等待的线程。
- park() 和 unpark() 是 LockSupport 类的静态方法,不需要和 Monitor 搭配使用
- unpark 可以在 park 之前执行
LockSupport 的 park() / unpark() 基于 Unsafe 的 park() / unpark(),Unsafe 提供了较为底层的操作内存、线程的方法。
public class LockSupport {
public static void park() {
if (Thread.currentThread().isVirtual()) {
// 虚拟线程
VirtualThreads.park();
} else {
// 调用Unsafe的park()
U.park(false, 0L);
}
}
private static final Unsafe U = Unsafe.getUnsafe();
}
方法辨析
sleep & wait
- sleep() 是 Thread 的静态方法,wait() 是 Object 的方法;
- sleep() 不需要和 synchronized 搭配使用,wait() 需要和 synchronized 搭配使用;
- sleep() 不会释放锁对象,wait() 会释放锁对象。
wait & park
- notify() 必须在 wait() 之后执行,unpark() 可在 park() 之前执行;
- wait() 需要和 synchronized 搭配使用,park() 不需要和 synchronized 搭配使用;
- notify() / notifyAll() 只能唤醒随机一个或者全部等待线程;unpark() 可以精确唤醒特定线程。
END
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
如果觉得本文对您有一点点帮助,欢迎点赞转发,这会对我有非常大的帮助,咱们下期见!