Thread类涉及较多基本概念,理解基本概念是读源码的前提。
目前比较多的是如下两个版本,
版本1 注意在RUNNABLE和RUNNING之间是双向箭头,当线程切换时,原本正在执行的线程从RUNNING状态转移至RUNNABLE状态。
版本2(目前应用更多)
任务状态说明,
线程对象刚创建完成,但是还没有运行。在new Thread()之后没有调用start方法之前的状态。
父线程调用子线程的start方法后,该线程对象才在JVM中挂号(JVM知道该线程要搞事)。版本1中的解释是此时处于可运行状态,但是还没有被CPU选中。版本2中则是调用start方法后,无论是否被CPU选中都称为RUNNABLE状态。
只出现在版本1中,一旦被CPU选中,才能执行线程的run方法,此时才算真正意义上的运行。版本2中RUNNING状态的转化
转入RUNNABLE状态 当CPU切换到其他线程时,正在运行的线程状态变为RUNNABLE,因为run方法没有在被执行转入BLOCKED状态 正在执行的线程调用了wait或sleep方法时,就会进入BLOCKED状态。区别是wait方法会放下当前CPU使用权,而sleep方法继续占用CPU转入TERMINATED状态 run方法执行完毕或意外终止。或者正在被执行的线程被调用了stop方法阻塞,由RUNNABLE或RUNNING进入BLOCKED的原因有,
线程等待获得 monitor lock 锁,比如在等待进入 synchronized 修饰的代码块或方法时,会从 RUNNABLE 变成 BLOCKED正在执行的线程调用了wait或sleep方法后就会进入BLOCKED状态(版本1中的解释)。在版本2中,执行了wait方法后,会进入到WAITING或TIMED_WAITING状态。由BLOCKED进入RUNNABLE状态的时机,
阻塞操作结束,等待CPU再次选中的这段过程阻塞过程被打断,如其他线程调用了interrupt方法sleep方法设定的休眠时间结束后会回到RUNNABLE状态由于wait操作进入BLOCKED状态的线程,其他线程发出notify或notifyAll的信号时,会唤醒进入到RUNNABLE状态线程在BLOCKED状态下如果被调用了stop方法时会转入TERMINATED状态。
TERMINATED 状态意味着线程的生命周期已经走完。这是线程的终止状态。此状态的线程不会再转化为其它任何状态。
处于 RUNNING 或者 BLOCKED 状态的线程都有可能变为 TERMINATED 状态,但原因是类似的,如下:
线程运行正常结束线程运行异常终止JVM意外停止版本2中,遇到Object#wait、Thread#join、LockSupport#park这些方法时,线程会等待另一个线程完成特定动作之后才结束等待。二者的区别是TIMED_WAITING有设定等待时间。
注意,这几种状态并不是任务所有的状态,只是在 Java 源码中列举出了几种状态, Java 线程的处理方法都是围绕这几种状态的。
优先级代表线程执行的机会的大小,优先级高的可能先执行,低的可能后执行,在 Java 源码中,优先级从低到高分别是 1 到 10,线程默认 new 出来的优先级都是 5,源码如下,
// 最低优先级 public final static int MIN_PRIORITY = 1; // 普通优先级,也是默认的 public final static int NORM_PRIORITY = 5; // 最大优先级 public final static int MAX_PRIORITY = 10;请注意优先级大仅仅是概率会更大,并不意味着就一定能够先于优先级低的获取。和摇车牌号一个道理,即使现在中签概率是标准的数倍,但摇中依然摇摇无期。而身边却时不时的出现第一次摇号就中的人。如果在 CPU 比较空闲的时候,那么优先级就没有用了。
默认创建的线程都是非守护线程。
创建守护线程时,需要将 Thread 的 daemon 属性设置成 true,守护线程的优先级很低,当 JVM 退出时,是不关心有无守护线程的,即使还有很多守护线程,JVM 仍然会退出。
守护线程类似于餐厅清洁员,一直在默默地做打扫卫生的工作。这个工作相对独立,不需要和别的角色有什么交互。而当其他所有人都不工作了,也就没有工作的必要了,因为不会有新的垃圾产生。那么可以下班,餐厅也就关门了。
ClassLoader可以简单理解成类加载器,就是把类从文件、二进制数组、URL 等位置加载成可运行 Class。
无返回值的线程初始化方式有两种,
start方法源码如下,
public synchronized void start() { // 如果没有初始化,抛异常 if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); // started 是个标识符,动作发生之前标识符是 false,发生完成之后变成 true boolean started = false; try { // 这里会创建一个新的线程,执行完成之后,新的线程已经在运行了,既 target 的内容已经在运行了 start0(); // 这里执行的还是主线程 started = true; } finally { try { // 如果失败,把线程从线程组中删除 if (!started) { group.threadStartFailed(this); } // Throwable 可以捕捉一些 Exception 捕捉不到的异常,比如说子线程抛出的异常 } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } // 开启新线程使用的是 native 方法 private native void start0();start方法主要逻辑如下,
检查线程状态,判断是否可启动将子线程加入到线程组中调用start0方法start方法并不调用run方法,而是调用start0方法。start0方法是native方法,也称为JNI(Java Native Interface)方法。JNI方法是java和其它语言交互的方式。同样也是java代码和虚拟机交互的方式,虚拟机就是由C++和汇编所编写。
start0会进入JVM执行,run方法的执行在源码中无法找到,但是在start方法的注释中有这样一句话,
the Java Virtual Machine* calls the run method of this thread.
所以run方法的调用过程如下,
这种就是实现 Runnable 的接口,并作为 Thread 构造器的入参。
调用时使用了两种方式,可以根据情况选择使用 start 或 run 方法,
使用 start 会开启子线程来执行 run 里面的内容使用 run 方法执行的还是主线程run方法源码如下,
public void run() { if (target != null) { target.run(); } }将Runnable对象传入Thread类中,调用run方法的过程,
看若兄弟,实为父子。Thread类的定义如下,
public class Thread implements RunnableThread实现了Runnable接口。
Thread类的run方法上有@Override注解。所以继承thread类实现多线程,其实也相当于是实现runnable接口的run方法。此时,不需要再传入一个Runnable类去启动。它自己已具备了thread的功能,自己就可以运转起来。
Thread类也实现了Runnable接口,那么Thread子类对象也可以传入另外的Thread对象,让其执行自己的run方法。
源码关键部分如下,
// 无参构造器,线程名字自动生成 public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } // g 代表线程组,线程组可以对组内的线程进行批量的操作 // target 是Runnable对象 // name 线程的名字,自定义或自动生成 // stackSize 可以设置堆栈的大小 private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name.toCharArray(); // 创建Thread对象的线程作为父线程 Thread parent = currentThread(); this.group = g; // 子线程会继承父线程的守护属性 this.daemon = parent.isDaemon(); // 子线程继承父线程的优先级属性 this.priority = parent.getPriority(); // classLoader if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); // 当父线程的 inheritableThreadLocals 的属性值不为空时 // 会把 inheritableThreadLocals 里面的值全部传递给子线程 if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this.stackSize = stackSize; /* Set thread ID */ // 线程 id 自增 tid = nextThreadID(); }