前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程的实现与分析

线程的实现与分析

作者头像
itliusir
发布2020-01-15 10:15:30
3200
发布2020-01-15 10:15:30
举报
文章被收录于专栏:刘君君刘君君

摘要:

  1. Java 的线程模型
  2. Java 的线程状态
  3. Java线程的 join 实现原理

TOP 带着问题来分析

  1. Java 的线程模型
  2. Java 的线程状态
  3. Java线程的 join 实现原理

1. 线程模型

线程是操作系统调度的最小单位,实现线程有三种方式,而 Java Thread 采用的是 内核线程实现

1.1 用户层实现(N:1)

优点:
  • 用户线程切换不需要内核介入,切换很快、代价相对低
  • 用户线程操作成本低
缺点:
  • 用户线程自己进行管理,比较麻烦
  • 阻塞处理复杂
  • 如何将线程映射到其他处理器

1.2 内核线程实现(1:1)

每一个用户线程对应一个内核线程,内核去完成线程的创建和调度。

优点:
  • 阻塞处理比较简单
  • 充分利用硬件
缺点:
  • 用户线程操作涉及到内核调用,代价较高

通过跟踪 Java Thread 中 start() 方法: start() -> native start0() -> JVM_StartThread() -> JavaThread() -> os::create_thread() -> pthread_create() 可以发现在 Java 中每次调用 start() 方法,都会在 C++ 的 JavaThread 构造方法里通过 pthread 进行创建一个内核线程。 回到问题 TOP 1 ,也就是说 Java 采用的线程模型是 1:1

1.3 混合实现(M:N)

优点:
  • 用户线程的操作成本低
  • 充分利用硬件
  • 阻塞问题折中处理
缺点:
  • 需要用户层管理和映射

2. 线程状态分析

在状态枚举类 java.lang.Thread.State 中我们看到了一共有 6 种状态

  • NEW 新建状态,线程还没调用 start() 方法
  • RUNNABLE 可运行状态,调用 start() 方法后,正在运行或等待系统资源
  • BLOCKED 阻塞状态,等待锁、Object#wait()-notify() 后都会进入阻塞状态
  • WAITING 等待状态,调用 Object#wait()、join()、LockSupport#park() ,注意这里都是没有超时时间
  • TIMED_WAITING 超时等待状态,调用 Thread#sleep()、Object#wait(long)、join(long)、LockSupport#parkNanos、LockSupport#parkUntil,可以发现这里都是有超时时间
  • TERMINATED 终止状态,线程已经执行完毕

2.1 线程状态流程图

回到问题 TOP 2 ,相信看完这个流程图会对状态流转更清晰一些

3. 核心方法分析

3.1 join()

例如我们 主线程A 要等待 子线程B 完成再往下执行,可以调用 子线程B 的join() 方法

回到问题 TOP 3 ,其实就是通过 wait 方法来让主线程等待,最后子线程完成后会唤醒主线程来实现了一个线程之间的通信。

代码语言:javascript
复制
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;
        }
    }
}

3.2 interrupt()

我们知道 stop() 方法由于太暴力和不安全已经设置为过期,现在基本上是采用 interrupt() 来交给我们优雅的处理。为什么这样说呢?

我们看下面 interrupt() 的实现可以看到实际上只是设置了一个中断标识(通知线程应该中断了),并不会真正停止一个线程。

代码语言:javascript
复制
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();
}

但是我们可以通过这个中断的通知来自己处理是继续运行还是中断,例如我们想要中断后停止线程:

代码语言:javascript
复制
Thread thread = new Thread(() -> {
    while (!Thread.interrupted()) {
        // do more work.
    }
});
thread.start();

// 一段时间以后
thread.interrupt();

需要注意的是,在一些可中断阻塞函数中,会抛出 InterruptedException,需要注意的是如果你不想处理继续往上抛,需要再次调用 interrupt() 方法(因为中断状态已经被重置了)。

4. 总结

我们这篇文章主要是分析了线程模型和线程的状态,已经几个核心方法的实现,相信看完会对线程有了更深一层的认识。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • TOP 带着问题来分析
  • 1. 线程模型
    • 1.1 用户层实现(N:1)
      • 优点:
      • 缺点:
    • 1.2 内核线程实现(1:1)
      • 优点:
      • 缺点:
    • 1.3 混合实现(M:N)
      • 优点:
      • 缺点:
  • 2. 线程状态分析
    • 2.1 线程状态流程图
    • 3. 核心方法分析
      • 3.1 join()
        • 3.2 interrupt()
        • 4. 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档