摘要:
线程是操作系统调度的最小单位,实现线程有三种方式,而 Java Thread 采用的是 内核线程实现
每一个用户线程对应一个内核线程,内核去完成线程的创建和调度。
通过跟踪 Java Thread 中 start() 方法: start() -> native start0() -> JVM_StartThread() -> JavaThread() -> os::create_thread() -> pthread_create() 可以发现在 Java 中每次调用 start() 方法,都会在 C++ 的 JavaThread 构造方法里通过 pthread 进行创建一个内核线程。 回到问题 TOP 1 ,也就是说 Java 采用的线程模型是 1:1
在状态枚举类 java.lang.Thread.State 中我们看到了一共有 6 种状态
回到问题 TOP 2 ,相信看完这个流程图会对状态流转更清晰一些
例如我们 主线程A 要等待 子线程B 完成再往下执行,可以调用 子线程B 的join() 方法
回到问题 TOP 3 ,其实就是通过 wait 方法来让主线程等待,最后子线程完成后会唤醒主线程来实现了一个线程之间的通信。
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
// 获取当前时间
long base = System.currentTimeMillis();
long now = 0;
// 入参校验
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 默认 join()参数走这个分支
if (millis == 0) {
// 只要线程还未完成
while (isAlive()) {
// 0 是一直等待下去
wait(0);
}
} else {
while (isAlive()) {
// 还剩下多少时间
long delay = millis - now;
// 时间到了就跳出
if (delay <= 0) {
break;
}
// 调用wait方法等待
wait(delay);
// 当前已经过了多久
now = System.currentTimeMillis() - base;
}
}
}
我们知道 stop() 方法由于太暴力和不安全已经设置为过期,现在基本上是采用 interrupt() 来交给我们优雅的处理。为什么这样说呢?
我们看下面 interrupt() 的实现可以看到实际上只是设置了一个中断标识(通知线程应该中断了),并不会真正停止一个线程。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
但是我们可以通过这个中断的通知来自己处理是继续运行还是中断,例如我们想要中断后停止线程:
Thread thread = new Thread(() -> {
while (!Thread.interrupted()) {
// do more work.
}
});
thread.start();
// 一段时间以后
thread.interrupt();
需要注意的是,在一些可中断阻塞函数中,会抛出 InterruptedException,需要注意的是如果你不想处理继续往上抛,需要再次调用 interrupt() 方法(因为中断状态已经被重置了)。
我们这篇文章主要是分析了线程模型和线程的状态,已经几个核心方法的实现,相信看完会对线程有了更深一层的认识。