浅析-Thread

12次阅读

共计 6827 个字符,预计需要花费 18 分钟才能阅读完成。

零 前期准备

0 FBI WARNING

文章异常啰嗦且绕弯。

1 版本

JDK 版本 : OpenJDK 12.0.1

IDE : idea 2019.2

2 Thread 简介

Thread 对象是 java 中线程的封装对象,用于控制线程的一切属性,也是 java 中最常用到的基础对象之一。

一 正文

1 Thread 类和成员变量

1.1 Thread 和 Runnable

在工程实践中 Thread 经常与 Runnable 一同使用,用于运行多线程程序。而本质上 Thread 也实现了 Runnnable:

// Thread.class
public class Thread implements Runnable {

    // 忽略其它成员变量和方法,暂时先只关注 Runnable 相关的方法
    // ...
    
    // 在创建 Thread 对象的时候可以传入的 Runnable 对象
    // 如果不传入则 run() 方法无效
    private Runnable target;

    // 执行 target 对象
    // 由此可见 Thread 本身其实是一个静态代理的典例
    @Override
    public void run() {if (target != null) {target.run();
        }
    }
}

1.2 成员变量

// 线程名称
private volatile String name;

// 线程优先级,数字越大优先级越高
private int priority;
// 最小优先级
public static final int MIN_PRIORITY = 1;
// 默认优先级
public static final int NORM_PRIORITY = 5;
// 最高优先级
public static final int MAX_PRIORITY = 10;

// 是否是守护线程,默认为 否
private boolean daemon = false;

/* Fields reserved for exclusive use by the JVM */
private boolean stillborn = false;
private long eetop;

// Runnable
private Runnable target;

// 该线程对象属于哪个线程组
private ThreadGroup group;

// 类加载器
private ClassLoader contextClassLoader;

private AccessControlContext inheritedAccessControlContext;

private static int threadInitNumber;

// 线程绑定的 ThreadLocal
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

// 栈大小
private final long stackSize;

private long nativeParkEventPointer;

// 线程 id
private final long tid;

private static long threadSeqNumber;

// 线程状态
private volatile int threadStatus;

// 用来作为锁的对象
volatile Object parkBlocker;
// 用来作为锁的对象
private final Object blockerLock = new Object();


private volatile Interruptible blocker;

2 构造器

Thread 有许多重载的构造器,但是基本上都只是在值数量上做了封装,核心的构造器逻辑只有一个:

// Thread.class
private Thread(ThreadGroup g, Runnable target, String name,
                   long stackSize, AccessControlContext acc,
                   boolean inheritThreadLocals) {
    // 有效性验证
    if (name == null) {throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    // currentThread() 方法会获取到当前线程的线程对象
    // 由于当前线程是创建该线程的线程,所以是该线程对象的 父线程
    Thread parent = currentThread();
    
    // 权限控制器
    SecurityManager security = System.getSecurityManager();
    if (g == null) {if (security != null) {g = security.getThreadGroup();
        }

        if (g == null) {
            // 如果没有组的话,会获取到 父线程 的线程组赋值给子线程
            g = parent.getThreadGroup();}
    }

    g.checkAccess();

    // 权限控制器
    if (security != null) {if (isCCLOverridden(getClass())) {
            security.checkPermission(SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    // 存储线程组
    this.group = g;
    // 是否是守护线程,如果父线程是,那么子线程必然是
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    
    
    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);
    // threadLocal 配置
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    // 栈深度
    this.stackSize = stackSize;

    // 生成一个唯一的线程 id
    this.tid = nextThreadID();}

3 start

start() 方法是 Thread 对象启动新线程的核心方法:

// Thread.class

// step 1
public synchronized void start() {
    
    // 先判断线程的状态,如果线程状态不正确直接抛出错误
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    // 将对象添加到线程组
    group.add(this);

    // 线程是否正常启动的标识对象
    boolean started = false;
    try {
        // 调用 native 的线程启动方法
        start0();
        // 修改标识对象
        started = true;
    } finally {
        try {
            // 如果 started 仍然等于 false
            // 那么此处会在线程组中添加失败信息
            if (!started) {group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {// 此处没有做任何异常处理}
    }
}

// step 2
// native 方法启动线程
private native void start0();

4 sleep

sleep(…) 方法是用来休眠线程的常用方法:

// Thread.class

// step 1
public static void sleep(long millis, int nanos) throws InterruptedException {

    // 此方法中使用者可以输入 毫秒 和 纳秒 两个参数
    // 但是实际上 java 只支持到毫秒级的线程休眠
    // 如果纳秒数大于零,那么就会在毫秒数上 +1,意思是多休眠一毫秒
    // 这个方法预留出来不太清楚具体作用
    
    if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    // 在此处对毫秒数加一
    if (nanos > 0 && millis < Long.MAX_VALUE) {millis++;}
    // 调用 native 的休眠方法
    sleep(millis);
}

// step 2
// native 方法休眠线程
public static native void sleep(long millis) throws InterruptedException;

5 interrupt

interrupt() 方法是用来中断线程的常用方法:

// Thread.class

// step 1
public void interrupt() {
    // 如果当前方法的调用线程不是此线程对象代表的线程,则进入该段逻辑
    // 此处会优先使用 Interruptible(中断器) 去中断当前线程
    // 如果没有配置中断器,那么调用自身的 native 方法去中断线程
    if (this != Thread.currentThread()) {// checkAccess() 方法用于确认权限控制器
        checkAccess();

        synchronized (blockerLock) {
            // 此处可以自定义中断器
            Interruptible b = blocker;
            if (b != null) {b.interrupt(this);
                return;
            }
        }
    }
    
    // 调用 native 的线程中断方法
    interrupt0();}

// step 2
private native void interrupt0();

另外关于线程的中断,还有一些方法:

// 判断当前线程的线程状态
public static boolean interrupted() {// currentThread() 方法会获取到当前线程的线程对象
    // 然后判断此线程对象是否被中断了
    // 如果是被中断的,那么此处恢复线程
    return currentThread().isInterrupted(true);
}

public boolean isInterrupted() {
    // 不会恢复线程的中断状态
    return isInterrupted(false);
}

@HotSpotIntrinsicCandidate
private native boolean isInterrupted(boolean ClearInterrupted);

5 join

join() 方法是用阻塞调用线程,等待对象线程结束之后再执行之后操作的方法:

// Thread.class
// step 1
public final void join() throws InterruptedException {join(0);
}

// step 2
public final synchronized void join(long millis, int nanos) throws InterruptedException {
    // 参数有效性效验
    if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");
    }

    // 在这里判断 nanos 的值,如果在合理范围内,就在 millis 上加一
    // 这里可知,其实 join(...) 方法并不支持纳秒级别的时间间隔
    if (nanos > 0 && millis < Long.MAX_VALUE) {millis++;}

    // 调用 step 3 中的 join(...) 方法
    join(millis);
}

// step 3
// 核心的 join(...) 方法,具体的阻塞逻辑
public final synchronized void join(final long millis) throws InterruptedException {
    // 如果设置了超时时间,那么超过时间了就会停止阻塞
    if (millis > 0) {if (isAlive()) {final long startTime = System.nanoTime();
            long delay = millis;
            do {wait(delay);
            } while (isAlive() 
                    && (delay = millis - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
        }
    } else if (millis == 0) {
        // 不设置超时时间,此处一直等待直到执行完毕
        while (isAlive()) {wait(0);
        }
    } else {throw new IllegalArgumentException("timeout value is negative");
    }
}

由上述源码可知,join(…) 方法的本质是阻塞当前线程,一直到目标的线程对象不再存活 (isAlive() == false) 为止。

6 主要的 native 方法

实际上 Thread 是一个非常底层的类,大多数的方法都是 native 修饰的,即底层调用了 c 语言写的代码。

// 获取当前线程的线程对象
public static native Thread currentThread();

// 让出线程的时间片,即为短暂暂停线程的活动让给其它线程使用
public static native void yield();

7 其它方法

Thread 中还有一些特殊的和预留的方法:

// jdk9 中新加的空方法,暂不清楚用途
 @HotSpotIntrinsicCandidate
 public static void onSpinWait() {}

// 复写了 Object 的 clone() 方法,但是只要被调用就会报错
// 说明 Thread 对象不可被复制
@Override
protected Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException();
}

// 退出线程,逻辑很简单,即为置空成员变量,帮助 gc
private void exit() {if (threadLocals != null && TerminatingThreadLocal.REGISTRY.isPresent()) {TerminatingThreadLocal.threadTerminated();
    }
    if (group != null) {group.threadTerminated(this);
        group = null;
    }

    target = null;
    threadLocals = null;
    inheritableThreadLocals = null;
    inheritedAccessControlContext = null;
    blocker = null;
    uncaughtExceptionHandler = null;
}

// 判断线程组内活跃的线程数量
public static int activeCount() {return currentThread().getThreadGroup().activeCount();
}

// 设置为守护线程
public final void setDaemon(boolean on) {
    // 此处设置权限
    checkAccess();
    // 如果此线程此时还处于存活状态,那么就会报错
    // 即为,只有线程未启动的时候才能进行守护线程的设置
    if (isAlive()) {throw new IllegalThreadStateException();
    }
    daemon = on;
}

8 线程状态

java 中的线程的状态,以枚举对象的形式存在:

public enum State {
    NEW, // 新建状态,尚未启动
    RUNNABLE, // 可运行状态,即为运行状态
    BLOCKED, // 阻塞状态
    WAITING, // 等待状态
    TIMED_WAITING, // 限时等待状态
    TERMINATED; // 终止状态
}

本文仅为个人的学习笔记,可能存在错误或者表述不清的地方,有缘补充

正文完
 0