java线程

  从操作系统的角度,可以简单认为,线程是系统调度的最小单元,一个进程可以包含多个线程,作为任务的真正运作者,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等,但是会和进程内其他线程共享文件描述符、虚拟地址空间等。   具体实现中,线程还分为内核线程、用户线程,Java 的线程实现其实是与虚拟机相关的。对于我们最熟悉的 Sun/Oracle JDK,其线程也经历了一个演进过程,基本上在 Java 1.2 之后,JDK 已经抛弃用户调度的线程,现在的模型是一对一映射到操作系统内核线程

线程的状态

  在 Java 5 以后,线程状态被明确定义在其公共内部枚举类型 java.lang.Thread.State 中,源代码如下:

    public enum State {
//新建状态
        NEW,
//就绪
        RUNNABLE,
//阻塞
        BLOCKED,

//等待
        WAITING,

//计时等待
        TIMED_WAITING,
//终止
        TERMINATED;
    }

各状态的具体含义如下:

  • 新建(NEW):线程被创建出来还没真正启动的状态,可以认为它是个 Java 内部状态。
  • 就绪(RUNNABLE):线程已经在 JVM 中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它 CPU 片段,在就绪队列里面排队。从API的角度看没有所谓的正在运行状态(RUNNING)
  • 阻塞(BLOCKED):阻塞表示线程在等待 Monitor lock。比如,线程试图通过 synchronized 去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。
  • 等待(WAITING):正在等待其他线程采取某些操作。一个常见的场景是类似生产者消费者模式,发现任务条件尚未满足,就让当前消费者线程等待(wait),另外的生产者线程去准备任务数据,然后通过类似 notify 等动作,通知消费线程可以继续工作了。Thread.join() 也会令线程进入等待状态。
  • 计时等待(TIMED_WAIT):其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如 wait 或 join 等方法的指定超时版本。
  • 终止(TERMINATED):不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。 状态之间的切换如下图所示:

线程状态切换图

其它知识

  • 可以通过setDaemon方法将线程设为守护线程,JVM 发现只有守护线程存在时,将结束进程。
  • 在多核 CPU 的系统中,线程等待存在一种可能,就是在没有任何线程广播或者发出信号的情况下,线程就被唤醒,如果处理不当就可能出现诡异的并发问题,所以我们在等待条件过程中,建议采用下面模式来书写。
// 推荐
while ( isCondition()) {
waitForAConfition(...);
}

// 不推荐,可能引入 bug
if ( isCondition()) {
waitForAConfition(...);
}

比如线程里的join方法采用的就是上面的书写方式,源代码如下:

    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");
        }

        if (millis == 0) {
//通过 while循环验证线程状态。
            while (isAlive()) {
//join内部是让被调用线程调用wait(0)操作来进行实现的
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
  • 可以通过如下方式打印系统有的线程,代码如下:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class Test {

    public static void main(String[] args) throws Exception {
        
        System.out.println("hello");
        // 获取java的线程管理MXBean
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        // 不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息
        ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false);
        // 遍历线程信息,仅打印线程id和线程名称信息
        for (ThreadInfo info : threadInfo) {
            System.out.println(info.getThreadId() + "--" + info.getThreadName() + "--" + info.getThreadState().name());
        }
        
    }
}

可以看出一个空的main方法会有如下几个线程: 1:main线程。 2:Reference Handler:处理引用对象本身的垃圾回收。 3:Finalizer:处理类的Finalizer方法。 4:Signal Dispatcher:外部jvm命令的转发器。 5:Attach Listener:负责接收外部命令的,如jmap、jstack。

  • 线程interrupt()方法不会中断一个正在运行的线程。它的作用是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。java源码如下:
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) { //默认b为空,不会进入这个方法
                interrupt0();           // Just to set the interrupt flag
//这里才会真正的中断线程
                b.interrupt(this);
                return;
            }
        }
        interrupt0(); //设置interrupt的状态并清除
    }
  • interrupted方法用于测试当前线程是否已经中断。属于static方法,将清除线程的interrupt状态。
  • isInterrupted方法同样有于测试线程是否已经中断,不同的是这个方法属于Thread。并且不会影线程的interrupt状态。方法源码如下:
//属于Thread对象的方法
    public boolean isInterrupted() {
//传入的参数是false,不会清除Thread的interrupt状态
        return isInterrupted(false);
    }

//属于static方法
    public static boolean interrupted() {
//得到当前线程,并调用 isInterrupted, 传入的参数是true。会清除当前线程的interrupt状态 
        return currentThread().isInterrupted(true);
    }

//这是个本地方法
    /**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏python3

python3--进程同步(multiprocess.Lock, Semaphore, Event)

通过之前的学习,实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO...

73530
来自专栏王亚昌的专栏

对数据操作封装的一点心得

在对数据进行操作时,可能需要读写name,于是我们写了一个接口,这个接口会实时更新缓存

8010
来自专栏腾讯移动品质中心TMQ的专栏

像 google 一样测试系列之四:技术篇

Android 白盒测试覆盖率低的最主要原因,是大部分人都没有测到 Android 层,只测试了Java层部分,导致覆盖率低。亲,你是不是认为Android层的...

37710
来自专栏码农阿宇

asp.net core轻松入门之MVC中Options读取配置文件

接上一篇中讲到利用Bind方法读取配置文件 ASP.NET Core轻松入门Bind读取配置文件到C#实例 那么在这篇文章中,我将在上一篇文章的基础上,利...

27340
来自专栏JavaEdge

Java线程状态

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态 英文翻译过来是线程还是没有开始执行。 首先,既然已经...

562110
来自专栏进击的程序猿

php异常处理 之 BooBoo库介绍

本文介绍php开源库BooBoo,是一个处理php异常和错误的开源库,通过简单的分析代码,我们知道了实际项目中怎么正确的设置错误和异常。

11220
来自专栏Linux驱动

35.QT-多线程

比如,当下载多个文件时,该下载相关的进程就会创建多个线程,每个线程负责下载一个文件

37720
来自专栏IMWeb前端团队

上手 yeoman generator

最近折腾脚手架相关的一些事情。说到脚手架,不得不谈的就是yeoman了。 是什么 yeoman是一个脚手架生成工具。 yeoman generator则是yeo...

24750
来自专栏张善友的专栏

Contact Manager Web API 示例[2] Web API Routing

联系人管理器web API是一个Asp.net web api示例程序,演示了通过ASP.NET Web API 公开联系信息,并允许您添加和删除联系人,示例地...

19490
来自专栏张善友的专栏

Contact Manager Web API 示例[2] Web API Routing

联系人管理器web API是一个Asp.net web api示例程序,演示了通过ASP.NET Web API 公开联系信息,并允许您添加和删除联系人,示例地...

20460

扫码关注云+社区

领取腾讯云代金券