写点什么

Thread State 详解

作者:自然
  • 2022 年 8 月 10 日
    广东
  • 本文字数:2603 字

    阅读完需:约 9 分钟

前言

文本已收录至我的 GitHub 仓库,欢迎 Star:https://github.com/bin392328206/six-finger

种一棵树最好的时间是十年前,其次是现在

我知道很多人不玩 qq 了,但是怀旧一下,欢迎加入六脉神剑 Java 菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客

絮叨

多线程的系列前面已经写了很多了,有兴趣的可以去我的仓库康康,今天呢?我也是看到人家写的不错,然后自己随便抄袭下,哈哈,其实就是把看到的知识记录一下下。自己写一遍的话,可能记忆深刻一点。

线程状态转换图


  • NEW 初始状态

  • RUNNABLE 运行状态

  • BLOCKED 阻塞状态

  • WAITING 等待状态

  • TIME_WAITING 超时等待状态

  • TERMINATED 终止状态


注意:调用 obj.wait()的线程需要先获取 obj 的 monitor,wait()会释放 obj 的 monitor 并进入等待态。所以 wait()/notify()都要与 synchronized 联用。

阻塞与等待的区别

阻塞:当一个线程试图获取对象锁(非 java.util.concurrent 库中的锁,即 synchronized),而该锁被其他线程持有,则该线程进入阻塞状态。它的特点是使用简单,由 JVM 调度器来决定唤醒自己,而不需要由另一个线程来显式唤醒自己,不响应中断。


阻塞 一个线程因为等待临界区的锁被阻塞产生的状态


等待:当一个线程等待另一个线程通知调度器一个条件时,该线程进入等待状态。它的特点是需要等待另一个线程显式地唤醒自己,实现灵活,语义更丰富,可响应中断。例如调用:Object.wait()、Thread.join()以及等待 Lock 或 Condition。等待 一个线程进入了锁,但是需要等待其他线程执行某些操作


需要强调的是虽然 synchronized 和 JUC 里的 Lock 都实现锁的功能,但线程进入的状态是不一样的。synchronized 会让线程进入阻塞态,而 JUC 里的 Lock 是用 LockSupport.park()/unpark()来实现阻塞/唤醒的,会让线程进入等待态。但话又说回来,虽然等锁时进入的状态不一样,但被唤醒后又都进入 runnable 态,从行为效果来看又是一样的。一个线程进入了锁,但是需要等待其他线程执行某些操作

主要操作

start()

新启一个线程执行其 run()方法,一个线程只能 start 一次。主要是通过调用 native start0()来实现。


public synchronized void start() {     //判断是否首次启动        if (threadStatus != 0)            throw new IllegalThreadStateException();
group.add(this);
boolean started = false; try {       //启动线程 start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }
private native void start0();
复制代码

run()

run()方法是不需要用户来调用的,当通过 start 方法启动一个线程之后,当该线程获得了 CPU 执行时间,便进入 run 方法体去执行具体的任务。注意,继承 Thread 类必须重写 run 方法,在 run 方法中定义具体要执行的任务。

sleep()

sleep 方法有两个重载版本


sleep(long millis)     //参数为毫秒
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
复制代码


sleep 相当于让线程睡眠,交出 CPU,让 CPU 去执行其他的任务。


但是有一点要非常注意,sleep 方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用 sleep 方法,其他线程也无法访问这个对象。

yield()

调用 yield 方法会让当前线程交出 CPU 权限,让 CPU 去执行其他的线程。它跟 sleep 方法类似,同样不会释放锁。但是 yield 不能控制具体的交出 CPU 的时间,另外,yield 方法只能让拥有相同优先级的线程有获取 CPU 执行时间的机会。


注意,调用 yield 方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取 CPU 执行时间,这一点是和 sleep 方法不一样的。

join()

join 方法有三个重载版本


1 join()2 join(long millis)     //参数为毫秒3 join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒
复制代码


join()实际是利用了 wait(),只不过它不用等待 notify()/notifyAll(),且不受其影响。它结束的条件是:1)等待时间到;2)目标线程已经 run 完(通过 isAlive()来判断)。


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"); } //0则需要一直等到目标线程run完 if (millis == 0) { while (isAlive()) { wait(0); } } else { //如果目标线程未run完且阻塞时间未到,那么调用线程会一直等待。 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } }}
复制代码

interrupt()

此操作会将线程的中断标志位置位,至于线程作何动作那要看线程了。


  • 如果线程 sleep()、wait()、join()等处于阻塞状态,那么线程会定时检查中断状态位如果发现中断状态位为 true,则会在这些阻塞方法调用处抛出 InterruptedException 异常,并且在抛出异常后立即将线程的中断状态位清除,即重- 新设置为 false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

  • 如果线程正在运行、争用 synchronized、lock()等,那么是不可中断的,他们会忽略。


可以通过以下三种方式来判断中断:


  • isInterrupted()

  • 此方法只会读取线程的中断标志位,并不会重置。

  • interrupted()

  • 此方法读取线程的中断标志位,并会重置。

  • throw InterruptException

  • 抛出该异常的同时,会重置中断标志位。

结尾

小六六觉得,最主要的是理解一下 阻塞 和等待 这 2 个状态的区别,等待和超时等待很好理解,就是说你们的线程需要等待其他线程操作的时间是确定的。


文章出自 Thread详解


日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉


创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见


六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !

发布于: 刚刚阅读数: 3
用户头像

自然

关注

还未添加个人签名 2020.03.01 加入

小六六,目前负责营收超百亿的支付中台

评论

发布
暂无评论
Thread  State 详解_Thread_自然_InfoQ写作社区