并发编程:Java常用线程调度方法及实现原理

本文将介绍Java常用线程调度方法及实现原理,包括sleep、wait&notify、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

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。

如果觉得本文对您有一点点帮助,欢迎点赞转发,这会对我有非常大的帮助,咱们下期见!

  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值