天天看点

终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式

1 类注释

程序中执行的线程。JVM允许应用程序拥有多个并发运行的执行线程。

每个线程都有一个优先级。优先级高的线程优先于优先级低的线程执行。每个线程可能被标记为守护线程,也可能不被标记为守护线程。

当在某个线程中运行的代码创建一个新 Thread 对象时,新线程的优先级最初设置为创建线程的优先级,并且只有在创建线程是一个守护线程时,新线程才是守护线程。

当JVM启动时,通常有一个非守护的线程(它通常调用某个指定类的main方法)。JVM 继续执行线程,直到发生以下任何一种情况时停止:

Runtime 类的 exit 方法已被调用,且安全管理器已允许执行退出操作(比如调用 Thread.interrupt 方法)

不是守护线程的所有线程都已死亡,要么从对 run 方法的调用返回,要么抛出一个在 run 方法之外传播的异常

每个线程都有名字,多个线程可能具有相同的名字,Thread 有的构造器如果没有指定名字,会自动生成一个名字。

2 线程的基本概念

2.1 线程的状态

源码中一共枚举了六种线程状态

终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式

线程的状态机

终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式

2.1.1 状态机说明

当调用start(),进入RUNNABLE,当前线程sleep()结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入RUNNABLE

当线程运行完成、被打断、被中止,状态都会从 RUNNABLE 变成 TERMINATED

如果线程正好在等待获得 monitor lock 锁,比如在等待进入 synchronized 修饰的代码块或方法时,会从 RUNNABLE 转至 BLOCKED

WAITING 和 TIMED_WAITING 类似,都表示在遇到 Object#wait、Thread#join、LockSupport#park 这些方法时,线程就会等待另一个线程执行完特定的动作之后,才能结束等待,只不过 TIMED_WAITING 是带有等待时间的

2.2 线程的优先级

优先级代表线程执行的机会的大小,优先级高的可能先执行,低的可能后执行,在 Java 源码中,优先级从低到高分别是 1 到 10,线程默认 new 出来的优先级都是 5,源码如下:

终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式

分别为最低,普通(默认优先级),最大优先级

2.3 守护线程

创建的线程默认都是非守护线程。

  • 创建守护线程时,需要将 Thread 的 daemon 属性设置成 true
  • 终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式
  • 守护线程的优先级很低,当 JVM 退出时,是不关心有无守护线程的,即使还有很多守护线程,JVM 仍然会退出。

在工作中,我们可能会写一些工具做一些监控的工作,这时我们都是用守护线程去做,这样即使监控抛出异常,也不会影响到业务主线程,所以 JVM 也无需关注监控是否正在运行,该退出就退出,所以对业务不会产生任何影响。

3 线程的初始化的两种方式

无返回值的线程主要有两种初始化方式:

3.1 继承 Thread

看下 start 方法的源码:

public synchronized void start() {
        /**
         * 对于由VM创建/设置的主方法线程或“系统”组线程,不调用此方法。
         * 将来添加到此方法中的任何新功能可能也必须添加到VM中。
         * 
         * 零状态值对应于状态“NEW”。
         * 因此,如果没有初始化,直接抛异常
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* 
         * 通知组此线程即将start,以便可以将其添加到组的线程列表中
         * 并且可以减少组的unstarted线程的计数
         */
        group.add(this);

        // started 是个标识符,在处理一系列相关操作时,经常这么设计
        // 操作执行前前标识符是 false,执行完成后变成 true
        boolean started = false;
        try {
            // 创建一个新的线程,执行完成后,新的线程已经在运行了,即 target 的内容已在运行
            start0();
            // 这里执行的还是 main 线程
            started = true;
        } finally {
            try {
                // 若失败,将线程从线程组中移除
                if (!started) {
                    group.threadStartFailed(this);
                }
            // Throwable 可以捕捉一些 Exception 捕捉不到的异常,比如子线程抛出的异常    
            } catch (Throwable ignore) {
                /* 
                 * 什么也不做。
                 * 如果start0抛出一个Throwable,那么它将被传递到调用堆栈
                 */
            }
        }
    }
    
    // 开启新线程使用的是 native 方法
    private native void start0();      

注意上面提到的的

threadStatus

变量

用于工具的Java线程状态,初始化以指示线程“尚未启动”

终于有人能把Thread讲清楚了(上)1 类注释2 线程的基本概念3 线程的初始化的两种方式