Thread类源码剖析

目录

1.引子

2.JVM线程状态

3.Thread常用方法

4.拓展点


一、引子

说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒。本文将剖析Thread类源码(本文后面源码全部默认JDK8),并讲解一些重要的拓展点。希望对大家能有一些帮助。

本文讲解主干全部出自源码和注释,保证了权威性。(注意:网上,某些书中很多观点都是错的,过时的,片面的,所以大家一定要看源码,重要事情说N遍,看源码!看源码!看源码......)

二、JVM线程状态

在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。

自JDK5开始,线程包括以下6个状态,摘自Thread.State:

 1     /**
 2      * A thread state.  A thread can be in one of the following states:
 3      * <ul>
 4      * <li>{@link #NEW}<br>
 5      *     A thread that has not yet started is in this state.
 6      *     </li>
 7      * <li>{@link #RUNNABLE}<br>
 8      *     A thread executing in the Java virtual machine is in this state.
 9      *     </li>
10      * <li>{@link #BLOCKED}<br>
11      *     A thread that is blocked waiting for a monitor lock
12      *     is in this state.
13      *     </li>
14      * <li>{@link #WAITING}<br>
15      *     A thread that is waiting indefinitely for another thread to
16      *     perform a particular action is in this state.
17      *     </li>
18      * <li>{@link #TIMED_WAITING}<br>
19      *     A thread that is waiting for another thread to perform an action
20      *     for up to a specified waiting time is in this state.
21      *     </li>
22      * <li>{@link #TERMINATED}<br>
23      *     A thread that has exited is in this state.
24      *     </li>
25      * </ul>
26      *
27      * <p>
28      * A thread can be in only one state at a given point in time.----》JVM中的线程必须只能是以上6种状态的一种。这些状态是JVM状态并不能和操作系统线程状态互相映射。
29      * These states are virtual machine states which do not reflect
30      * any operating system thread states.
31      *
32      * @since   1.5
33      * @see #getState
34      */
35     public enum State {
36         /**
37          * Thread state for a thread which has not yet started.
38          */
39         NEW,--->线程刚创建,还未执行(start方法)
40
41         /**
42          * Thread state for a runnable thread.  A thread in the runnable
43          * state is executing in the Java virtual machine but it may
44          * be waiting for other resources from the operating system
45          * such as processor.
46          */
47         RUNNABLE,--->已就绪可运行的状态。处于此状态的线程是正在JVM中运行的,但可能在等待操作系统级别的资源,例如CPU时间片
48
49         /**
50          * Thread state for a thread blocked waiting for a monitor lock.
51          * A thread in the blocked state is waiting for a monitor lock
52          * to enter a synchronized block/method or
53          * reenter a synchronized block/method after calling
54          * {@link Object#wait() Object.wait}.
55          */
56         BLOCKED,--->阻塞等待监视器锁。处于此状态的线程正在阻塞等待监视器锁,以进入一个同步块/方法,或者在执行完wait()方法后重入同步块/方法。
57
58         /**
59          * Thread state for a waiting thread.
60          * A thread is in the waiting state due to calling one of the
61          * following methods:
62          * <ul>
63          *   <li>{@link Object#wait() Object.wait} with no timeout</li>
64          *   <li>{@link #join() Thread.join} with no timeout</li>
65          *   <li>{@link LockSupport#park() LockSupport.park}</li>
66          * </ul>
67          *
68          * <p>A thread in the waiting state is waiting for another thread to
69          * perform a particular action.
70          *
71          * For example, a thread that has called <tt>Object.wait()</tt>
72          * on an object is waiting for another thread to call
73          * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
74          * that object. A thread that has called <tt>Thread.join()</tt>
75          * is waiting for a specified thread to terminate.
76          */
77         WAITING,--->等待。执行完Object.wait无超时参数操作,或者 Thread.join无超时参数操作(进入等待指定的线程执行结束),或者 LockSupport.park操作后,线程进入等待状态。                       一般在等待状态的线程在等待其它线程执行特殊操作,例如:等待另其它线程操作Object.notify()唤醒或者Object.notifyAll()唤醒所有。
78
79         /**
80          * Thread state for a waiting thread with a specified waiting time.
81          * A thread is in the timed waiting state due to calling one of
82          * the following methods with a specified positive waiting time:
83          * <ul>
84          *   <li>{@link #sleep Thread.sleep}</li>
85          *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
86          *   <li>{@link #join(long) Thread.join} with timeout</li>
87          *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
88          *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
89          * </ul>
90          */
91         TIMED_WAITING,--->限时等待。Thread.sleep、Object.wait带超时时间、Thread.join带超时时间、LockSupport.parkNanos、LockSupport.parkUntil这些操作会时线程进入限时等待。
92
93         /**
94          * Thread state for a terminated thread.
95          * The thread has completed execution.
96          */
97         TERMINATED;--->终止,线程执行完毕。
98     }

看了源码6种状态,很多人会迷惑怎么没有Running状态呢?好吧,请相信源码,不要混淆操作系统线程状态和java线程状态。JVM中的线程必须只能是以上6种状态的一种!(见上图枚举State 注释中的红色部分)。

Running其实是早期操作系统下“单线程进程”的状态,如下图:

 注意:上图已年久失修,不可参考!!!!

好吧,现在是不是觉得三观被颠覆...

最新JAVA(JVM)线程状态转换如下图

如上图,可见:RUNNABLE = 正在JVM中运行的(Running)+ 可能在等待操作系统级别的资源(Read),例如CPU时间片

  线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源),只有线程运行需要的所有条件满足了,才进入就绪状态。

  当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

  线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

  当由于突然中断或者子任务执行完毕,线程就会被消亡。

三.Thread类中的方法

老规矩,先看源码注释:

/**
 * A <i>thread</i> is a thread of execution in a program. The Java  ---》一个“线程”是在在程序中执行的线程。Java虚拟机允许应用多个线程并发运行。
 * Virtual Machine allows an application to have multiple threads of
 * execution running concurrently.
 * <p>
 * Every thread has a priority. Threads with higher priority are--》每个线程都有优先级,优先级高的先执行。线程可能是守护线程或者不是。
 * executed in preference to threads with lower priority. Each thread
 * may or may not also be marked as a daemon. When code running in
 * some thread creates a new <code>Thread</code> object, the new---》线程的优先级等于创建线程的优先级,当且仅当一个线程是守护线程,创建出来的线程才是守护线程
 * thread has its priority initially set equal to the priority of the
 * creating thread, and is a daemon thread if and only if the
 * creating thread is a daemon.
 * <p>
 * When a Java Virtual Machine starts up, there is usually a single--》通常JVM启动,有一个非守护线程作为主线程。只有当Runtime.exit被调用或者所有非守护线程死亡时(run执行完毕并返回/抛出异常)JVM会停止运行这些线程。
 * non-daemon thread (which typically calls the method named
 * <code>main</code> of some designated class). The Java Virtual
 * Machine continues to execute threads until either of the following
 * occurs:
 * <ul>
 * <li>The <code>exit</code> method of class <code>Runtime</code> has been
 *     called and the security manager has permitted the exit operation
 *     to take place.
 * <li>All threads that are not daemon threads have died, either by
 *     returning from the call to the <code>run</code> method or by
 *     throwing an exception that propagates beyond the <code>run</code>
 *     method.
 * </ul>
 * <p>
 * There are two ways to create a new thread of execution. One is to--》两种创建线程的方法:继承Thread类/实现Runnable接口
 * declare a class to be a subclass of <code>Thread</code>. This
 * subclass should override the <code>run</code> method of class
 * <code>Thread</code>. An instance of the subclass can then be
 * allocated and started. For example, a thread that computes primes
 * larger than a stated value could be written as follows:
 * <hr><blockquote><pre>
 *     class PrimeThread extends Thread {
 *         long minPrime;
 *         PrimeThread(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 *     }
 * </pre></blockquote><hr>
 * <p>
 * The following code would then create a thread and start it running:
 * <blockquote><pre>
 *     PrimeThread p = new PrimeThread(143);
 *     p.start();
 * </pre></blockquote>
 * <p>
 * The other way to create a thread is to declare a class that
 * implements the <code>Runnable</code> interface. That class then
 * implements the <code>run</code> method. An instance of the class can
 * then be allocated, passed as an argument when creating
 * <code>Thread</code>, and started. The same example in this other
 * style looks like the following:
 * <hr><blockquote><pre>
 *     class PrimeRun implements Runnable {
 *         long minPrime;
 *         PrimeRun(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 *     }
 * </pre></blockquote><hr>
 * <p>
 * The following code would then create a thread and start it running:
 * <blockquote><pre>
 *     PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
 * </pre></blockquote>
 * <p>
 * Every thread has a name for identification purposes. More than--》每个线程有自己的名称用来标识自己。但可能多个线程会重名,如果启动时没有创建名字,会自动生成一个。
 * one thread may have the same name. If a name is not specified when
 * a thread is created, a new name is generated for it.
 * <p>
 * Unless otherwise noted, passing a {@code null} argument to a constructor
 * or method in this class will cause a {@link NullPointerException} to be
 * thrown.
 *
 * @author  unascribed  --》意思是:该代码第一原作者不是我,但我实在也不知道是谁,就记作无名氏吧(版权意识)
 * @see     Runnable
 * @see     Runtime#exit(int)
 * @see     #run()
 * @see     #stop()
 * @since   JDK1.0
 */

  Thread类实现了Runnable接口,在Thread类中,

  关键属性

  name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,

  priority表示线程的优先级(最大值为10,最小值为1,默认值为5),

  daemon表示线程是否是守护线程,如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。在JVM中,垃圾收集器线程就是守护线程。

  target表示要执行的任务。

  group线程群组

  关键方法

  以下是关系到线程运行状态的几个方法:

  1)start

  start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

  2)run

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

  3)sleep

  sleep方法有两个重载版本:

1 public static native void sleep(long millis) throws InterruptedException;
2
3 public static void sleep(long millis, int nanos) throws InterruptedException; 

  sleep让线程睡眠,交出CPU,让CPU去执行其他的任务。sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。sleep方法相当于让线程进入阻塞状态。

  4)yield

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

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

  5)join

  join方法有三个重载版本:

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

  可以看出,当调用thread.join()方法后,main线程会进入等待,然后等待thread执行完之后再继续执行。

  实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:

  

  wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。

  6)interrupt

  interrupt,中断。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;

  7)stop

  stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。

  8)destroy

  destroy方法也是废弃的方法。基本不会被使用到。

四、

  

  



参考

《JAVA高并发程序设计》电子工业出版社

Java并发编程:Thread类的使用

时间: 2024-10-10 11:21:14

Thread类源码剖析的相关文章

tornado web.py Application类源码剖析

[课程]web2.0程序设计[作业要求]研究 application 对象源代码.说明 Application 对象实例化时,给出“debug=True”参数,代码动态自动编译的原理.[参考文档]Application 类源代码 tornado Application 官方文档 debug模式和自动重新加载 tornado.web提供了一个简单的Web框架的异步功能.一个请求处理程序的集合就组成了一个web application. 1.分析application类的源码我们知道,applica

老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类 每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行了事,而有些是需要在解析后创建相应的事件类实例并添加到命令队列里面排队执行.负责这部分工作的就是命令翻译类.那么我们往下还是继续在MonkeySourceNetwork这个范畴中MonkeyCommand类是怎么一回事: 图6-3-1 MonkeyCommand族谱 图中间的MonkeyCommand是一个接口,

Phaser实现源码剖析

在这里首先说明一下,由于Phaser在4.3代码里是存在,但并没有被开放出来供使用,但已经被本人大致研究了,因此也一并进行剖析. Phaser是一个可以重复利用的同步栅栏,功能上与CyclicBarrier和CountDownLatch相似,不过提供更加灵活的用法.也就是说,Phaser的同步模型与它们差不多.一般运用的场景是一组线程希望同时到达某个执行点后(先到达的会被阻塞),执行一个指定任务,然后这些线程才被唤醒继续执行其它任务. Phaser一般是定义一个parties数(parties一

fasttext源码剖析

fasttext源码剖析 目的:记录结合多方资料以及个人理解的剖析代码: https://heleifz.github.io/14732610572844.html http://www.cnblogs.com/peghoty/p/3857839.html 一:代码总体模块关联图: 核心模块是fasttext.cc以及model.cc模块,但是辅助模块也很重要,是代码的螺丝钉,以及实现了数据采取什么样子数据结构进行组织,这里的东西值得学习借鉴,而且你会发现存储训练数据的结构比较常用的手段,后期可

【安卓笔记】HandlerThread源码剖析

有时候我们需要在应用程序中创建一些常驻的子线程不定期地执行一些计算型任务,这时候可以考虑使用HandlerThread,它具有创建带消息循环的子线程的作用. 一.HanderThread使用示例 先熟悉下HandlerThread的一般用法.我们创建一个如下所示的Activity: package com.example.handlethreaddemo; import android.app.Activity; import android.os.Bundle; import android.

【安卓笔记】IntentService源码剖析

Service组件想必都不陌生,这里不费口舌了.强调一点,Service组件默认运行在UI线程,所以也是会阻塞主线程的,使用时切记不可在Service中执行耗时操作,而应该创建子线程,异步执行. IntentService类封装了在Service中创建子线程的工作(其实创建的是HandlerThread),我们只需继承IntentService,复写其onHandleIntent方法即可,onHandleIntent方法在子线程中运行,该方法的参数Intent来自onStart或者onStart

SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过service()方法,委派到doGet()或者doPost()这些方法,完成Http请求的处理. 在初始化流程中,SpringMVC巧妙的运用依赖注入读取参数,并最终建立一个与容器上下文相关联的spring子上下文.这个子上下文,就像Struts2中xwork容器一样,为接下来的Http处理流程中各种编程

《STL源码剖析》---stl_alloc.h阅读笔记

这一节是讲空间的配置与释放,但不涉及对象的构造和析构,只是讲解对象构造前空前的申请以及对象析构后空间怎么释放. SGI版本的STL对空间的的申请和释放做了如下考虑: 1.向堆申请空间 2.考虑了多线程.但是这节目的只是讲解空间配置与释放,因此忽略了多线程,集中学习空间的申请和释放. 3.内存不足时的应变措施 4.考虑到了内存碎片的问题.多次申请释放小块内存可能会造成内存碎片. 在C++中,内存的申请和释放是通过operator new函数和operator delete函数,这两个函数相当于C语

Android多线程研究(1)——线程基础及源码剖析

从今天起我们来看一下Android中的多线程的知识,Android入门容易,但是要完成一个完善的产品却不容易,让我们从线程开始一步步深入Android内部. 一.线程基础回顾 package com.maso.test; public class TraditionalThread { public static void main(String[] args) { /* * 线程的第一种创建方式 */ Thread thread1 = new Thread(){ @Override publi