Java虚拟机允许应用程序同时运行多个运行线程。
每个线程都有优先级,优先级高的线程可以被优先执行。当一个线程中创建了另一个新线程,新线程的优先级默认等于这个线程的优先级。只有守护线程创建的线程才是守护线程。
当一个Java虚拟机启动时,通常会启动一个非守护线程,来运行main方法。虚拟机将继续执行此线程,直到以下情况发生:
1)调用Runtime类的exit方法,并且安全管理器允许退出操作发生。
2)除了守护线程之外的所有线程都已经死亡。即线程内代码全部执行完返回,或线程运行时抛出异常。
每个线程都有一个名字,用于识别线程创建的目的。多个线程可能有相同的名字。若创建了一个没有指定名字的线程,则会自动为其生成一个名字。
Thread.java中的相关代码:
public class Thread implements Runnable {
// native方法
private static native void registerNatives();
// 静态代码块初始化
static {
registerNatives();
}
…
}
1.实现了Runnable接口,可以在线程中运行。
2.在静态代码块中调用registerNatives方法,确保该方法第一个执行。registerNatives方法用于动态注册native方法,将java方法和native方法进行关联。
Thread.java中的相关代码:
private volatile String name; // 用于记录线程的名字
private int priority; // 用于记录线程的优先级
private boolean daemon = false; // 用于表示线程是否为守护线程
private Runnable target; // 用于保存线程要执行的代码
private ThreadGroup group; // 用于记录线程所在的组
private ClassLoader contextClassLoader; // 用于保存上下文类加载器
// 用于根据封装的上下文对系统资源访问做出决策
private AccessControlContext inheritedAccessControlContext;
private static int threadInitNumber; // 用于匿名线程的自动编号
ThreadLocal.ThreadLocalMap threadLocals = null; // 用于保存当前线程共享的对象
// 用于保存从父线程继承的共享对象
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
private long stackSize; // 当前线程请求的堆栈大小,默认为0
private long tid; // 用于保存线程ID
private static long threadSeqNumber; // 用于自动生成线程ID
private volatile int threadStatus = 0; // 用于表示线程的状态,0表示线程尚未启动
// 在可中断I/O操作中阻塞此线程的对象(如果有的话)。
// 应在设置此线程的中断状态后调用拦截器的中断方法。
private volatile Interruptible blocker;
private final Object blockerLock = new Object(); // 用于同步锁
public final static int MIN_PRIORITY = 1; // 线程最小优先级
public final static int NORM_PRIORITY = 5; // 线程默认的优先级
public final static int MAX_PRIORITY = 10; // 线程最大优先级
// 允许子类重写上下文类加载器权限
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
new RuntimePermission("enableContextClassLoaderOverride");
用来表示线程的状态。
Thread.java中的相关代码:
public enum State {
// 尚未启动的线程的状态
NEW,
// 可运行的线程的状态
// 可能在Java虚拟机中执行,也可能在等在操作系统的其它资源
RUNNABLE,
// 获取不到锁的线程的状态
// 阻塞状态的线程可能正在等待获取锁来执行同步方法或代码块
// 或在调用wait方法释放锁后,又需要锁来执行同步方法或代码块
BLOCKED,
// 等待的线程的状态
// 等待状态的线程正在等待另一个线程的特定操作
// 调用wait方法不指定超时时间、调用join方法不指定超时时间,
// 线程将进入该状态
// 一个线程调用锁对象的wait方法,需要等待另一个线程调用该锁对象
// 的notify 或notifyAll方法
// 一个线程调用join方法,需要等待一个指定的线程终止
WAITING,
// 指定了等待时间等待的线程的状态
// 调用sleep方法、调用wait方法指定超时时间、调用join方法指定超时时间
// 线程将处于该状态
TIMED_WAITING,
// 终止的线程的状态
// 线程已经执行完毕
TERMINATED;
}
当创建线程不指定线程名称时,调用此方法产生编号作为线程名称的一部分。
Thread.java中的相关代码:
private static synchronized int nextThreadNum() {
// 返回,自增
return threadInitNumber++;
}
用于创建线程ID。
Thread.java中的相关代码:
private static synchronized long nextThreadID() {
// 自增,返回
return ++threadSeqNumber;
}
Thread.java中的相关代码:
public Thread() {
// 调用四个参数的init方法
// 默认线程名为”Thread-自增编号“
// 堆栈大小0,表示忽略该参数
init(null, null, "Thread-" + nextThreadNum(), 0);
}
Thread.java中的相关代码:
public Thread(Runnable target) {
// 调用四个参数的init方法
// 保存target,默认线程名为”Thread-自增编号“
// 堆栈大小0,表示忽略该参数
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread.java中的相关代码:
public Thread(String name) {
// 调用四个参数的init方法
// 堆栈大小0,表示忽略该参数
init(null, null, name, 0);
}
Thread.java中的相关代码:
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
// 调用四个参数的init方法
init(group, target, name, stackSize);
}
Thread所有的构造方法都调用该方法来实现,用来对线程进行初始化。
Thread.java中的相关代码:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
// 调用五个参数的init方法,默认AccessControlContext为空
init(g, target, name, stackSize, null);
}
线程初始化的核心实现。相比四个参数的init方法,多了一个AccessControlContext类型的参数。
Thread.java中的相关代码:
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;
// 获取创建当前线程的线程,相当于当前线程的父线程
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(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
// 通知线程组,增加一个未开始的线程
g.addUnstarted();
// 保存线程组到全局变量
this.group = g;
// 判断父线程是否为守护线程,将结果保存到全局变量
this.daemon = parent.isDaemon();
// 当前线程的优先级和父线程的优先级相同
this.priority = parent.getPriority();
// 若安全管理器没有开启,
// 或父线程的类的实例可以在不违反安全约束的条件下创建
if (security == null || isCCLOverridden(parent.getClass()))
// 调用方法,将父线程的上下文类加载器保存到全局变量
// 因为当父线程的类可以不违法安全约束创建实例,说明他可能存在子类,
// 即子类化。由于多态,调用getContextClassLoader可能由子类实现
this.contextClassLoader = parent.getContextClassLoader();
else // 若安全管理器开启,并且创建父线程的类的实例违反安全约束
// 调用静态变量,将父线程的上下文类加载器保存到全局变量
// 因为创建父线程类的实例违反安全规约,所以不可能存在子类
// 因此直接从父类的静态变量中获取
this.contextClassLoader = parent.contextClassLoader;
// 若存取控制上下文为空,从AccessController中获取
// 否则直接保存到全局变量
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
// 保存线程需要执行的Runnable对象
this.target = target;
// 设置线程优先级
setPriority(priority);
// 若父线程中存在继承的ThreadLocal
if (parent.inheritableThreadLocals != null)
// 则根据父进程中继承的ThreadLocal创建一个ThreadLocalMap对象
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
// 保存堆栈大小到全局变量
this.stackSize = stackSize;
// 设置线程ID
tid = nextThreadID();
}
获取当前执行的线程。
Thread.java中的相关代码:
public static native Thread currentThread();
设置线程优先级。
Thread.java中的相关代码:
public final void setPriority(int newPriority) {
ThreadGroup g;
// 检查当前运行的线程是否拥有修改此线程的权限
// 详解在1)处
checkAccess();
// 若要设置的线程优先级超过最大优先级或小于最小优先级,则抛出异常
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
// 获取线程组,若线程组不为空
// 详解在2)处
if((g = getThreadGroup()) != null) {
// 若要设置的优先级超过了线程组的最大优先级
if (newPriority > g.getMaxPriority()) {
// 将要设置的优先级改为线程组的最大优先级
newPriority = g.getMaxPriority();
}
// 将优先级保存到全局变量
// 调用setPriority0方法设置线程优先级
// 详解在3)处
setPriority0(priority = newPriority);
}
}
检查当前运行的线程是否拥有修改此线程的权限。
Thread.java中的相关代码:
public final void checkAccess() {
// 获取安全管理器
SecurityManager security = System.getSecurityManager();
// 若开启了安全管理
if (security != null) {
// 通过安全管理器检查当前运行的线程是否拥有修改此线程的权限
security.checkAccess(this);
}
}
获取线程组。
Thread.java中的相关代码:
public final ThreadGroup getThreadGroup() {
// 返回全局变量
return group;
}
设置线程优先级的核心方法。
Thread.java中的相关代码:
private native void setPriority0(int newPriority);
启动线程。
调用该方法将使JVM执行线程的run方法。
Thread.java中的相关代码:
public synchronized void start() {
// 若当前线程的状态不为0,即不是新创建尚未启动的状态
if (threadStatus != 0)
// 则抛出异常
throw new IllegalThreadStateException();
// 通知线程组,该线程已启动
group.add(this);
// 用于判断启动状态,设置false
boolean started = false;
try {
// 启动线程
// 详解在1)处
start0();
// 设置true
started = true;
} finally {
// 若发生异常
try {
// 若线程没有启动
if (!started) {
// 通知线程组线程启动失败
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 若start0方法抛出异常,则什么都不做,它的值会传给调用的堆栈。
}
}
}
线程启动的核心方法。
Thread.java中的相关代码:
private native void start0();
线程执行的核心方法。
若该线程通过传入Runnable对象创建,则调用Runnable的run方法。
若该线程通过继承Thread类实现,则需要重写Run方法。
Thread.java中的相关代码:
@Override
public void run() {
// 若target不为空,即线程通过传入Runnable对象创建
if (target != null) {
// 调用run方法
target.run();
}
}
该方法被系统调用,用于在线程退出前,对线程进行清理。
Thread.java中的相关代码:
private void exit() {
// 若线程组不为空
if (group != null) {
// 通知线程组
group.threadTerminated(this);
// 释放引用
group = null;
}
// 释放Runnable对象
target = null;
// 释放其它资源
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
用于中断一个线程。
Thread.java中的相关代码:
public void interrupt() {
// 若调用该方法的线程不是本线程
if (this != Thread.currentThread())
checkAccess(); // 检查权限,是否允许访问
// 同步锁
synchronized (blockerLock) {
// 获取中断拦截器
Interruptible b = blocker;
// 若中断拦截器不为空
if (b != null) {
// 设置线程中断标志位
// 详解在1)处
interrupt0();
// 调用中断拦截器的方法
b.interrupt(this);
// 返回
return;
}
}
// 若没有中断拦截器,直接设置线程中断标志位
interrupt0();
}
中断核心方法,该方法会设置线程中断标志位。
Thread.java中的相关代码:
private native void interrupt0();
判断当前线程是否中断。
调用该方法将清除线程的中断标志位。
Thread.java中的相关代码:
public static boolean interrupted() {
// 获取当前线程,调用isInterrupted方法判断
return currentThread().isInterrupted(true);
}
判断调用该方法的线程对象是否中断
Thread.java中的相关代码:
public boolean isInterrupted() {
// 调用了重载方法
return isInterrupted(false);
}
判断线程中断的核心方法。
中断状态的将被重置。重置的值不取决于参数ClearInterrupted。
Thread.java中的相关代码:
private native boolean isInterrupted(boolean ClearInterrupted);
判断线程是否存活。
存活即线程开始运行但并没有死亡的期间。
Thread.java中的相关代码:
public final native boolean isAlive();
获取线程优先级。
Thread.java中的相关代码:
public final int getPriority() {
// 直接返回
return priority;
}
设置线程的名字。
Thread.java中的相关代码:
public final synchronized void setName(String name) {
// 检查权限,是否允许访问
checkAccess();
// 若名字为空,则抛出异常
if (name == null) {
throw new NullPointerException("name cannot be null");
}
// 将线程名保存到全局变量
this.name = name;
// 若线程不是新创建未运行的状态
if (threadStatus != 0) {
// 通知native层处理
// 详解在1)处
setNativeName(name);
}
}
设置线程名字的核心方法。
Thread.java中的相关代码:
private native void setNativeName(String name);
获取线程的名字。
Thread.java中的相关代码:
public final String getName() {
// 直接返回
return name;
}
获取线程组。
Thread.java中的相关代码:
public final ThreadGroup getThreadGroup() {
// 直接返回
return group;
}
返回当前线程的线程组及其子线程组中所有活跃线程的数量。
Thread.java中的相关代码:
public static int activeCount() {
// 获取当前线程,获取线程组,遍历统计
return currentThread().getThreadGroup().activeCount();
}
当前线程等待在当前线程中调用该方法的线程死亡。
Thread.java中的相关代码:
public final void join() throws InterruptedException {
// 调用重载方法,超时时间为0
join(0);
}
若在指定时间内调用该方法的线程没有执行完毕,则等待的线程将不再等待。
Thread.java中的相关代码:
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
// 若指定毫秒小于0,则抛出异常
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 若纳秒小于0或大于999999,则抛出异常
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
// 若纳秒超过了500000,则化零为整,毫秒加一
// 若纳秒不为零且毫秒为0,则设置最小时间1毫秒
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
// 调用重载方法
join(millis);
}
重载的join方法。
最多等待mills毫秒来让线程死亡,参数为0表示永久等待。
Thread.java中的相关代码:
public final synchronized void join(long millis)
throws InterruptedException {
// 获取系统当前时间作为时间基准
long base = System.currentTimeMillis();
// 用于计算当前时间到base时间的间隔
long now = 0;
// 若等待时间小于0,则抛出异常
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 若等待时间为0,说明是永久等待
if (millis == 0) {
// 循环,若调用join方法的线程活跃
while (isAlive()) {
// 调用join方法所在的线程永久等待
wait(0);
}
} else { // 不是永久等待
// 循环,若调用join方法的线程活跃
while (isAlive()) {
// 计算还需要等待的时长
long delay = millis - now;
// 若还需要等待的时长小于0,则跳出循环
if (delay <= 0) {
break;
}
// 调用join方法所在的线程等待delay毫秒,超时放弃等待
wait(delay);
// 计算目前已经等待的时间
now = System.currentTimeMillis() - base;
}
}
}
注意:如在线程A中调用线程B的join方法,B为调用join方法的线程,A为调用join方法所在的线程。由于join方法中调用了wait方法,因此线程A阻塞等待,线程B正常执行。
用于标记一个线程是守护线程还是用户线程。
当唯一运行的线程为守护线程时,Java虚拟机退出。
该方法必须在线程开始前调用。
Thread.java中的相关代码:
public final void setDaemon(boolean on) {
// 检查权限,是否允许访问
checkAccess();
// 若线程活跃,即已经开始未结束,则抛出异常
if (isAlive()) {
throw new IllegalThreadStateException();
}
// 设置标志位
daemon = on;
}
判断线程是否为守护线程。
Thread.java中的相关代码:
public final boolean isDaemon() {
// 直接返回
return daemon;
}
返回线程对应的字符串形式。
Thread.java中的相关代码:
public String toString() {
// 获取线程组
ThreadGroup group = getThreadGroup();
// 若线程组存在
if (group != null) {
// 返回线程名,线程优先级,线程组名称
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else { // 若线程组不存在
// 返回线程名,线程优先级
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
获取线程的上下文类加载器。
Thread.java中的相关代码:
// 由于代码中调用getCallerClass方法,所以需要加上该注解
// 详解在1)处
@CallerSensitive
public ClassLoader getContextClassLoader() {
// 若线程上下文类加载器为空,则返回null
if (contextClassLoader == null)
return null;
//获取安全管理器
SecurityManager sm = System.getSecurityManager();
// 若开启了安全管理器
if (sm != null) {
// 检查类加载器的权限
ClassLoader.checkClassLoaderPermission(contextClassLoader,
Reflection.getCallerClass());
}
// 返回上下文类加载器
return contextClassLoader;
}
jvm的开发者认为jdk内有一些方法是危险的,不希望开发者调用,因此将这些方法通过CallerSensitive注解修饰。
所有内部调用getCallerClass方法的方法都必须使用@CallerSensitive进行注解,该方法用来获取调用该方法的类。在获取调用该方法的类时,会跳过调用链路上所有被@CallerSensitive直接修饰的方法所在的类,避免了多层反射调用时产生不安全的隐患。
为线程设置上下文类加载器。
Thread.java中的相关代码:
public void setContextClassLoader(ClassLoader cl) {
// 获取安全管理器
SecurityManager sm = System.getSecurityManager();
// 若开启了安全管理器
if (sm != null) {
// 检查权限,是否允许设置上下文类加载器
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
}
// 保存到全局变量
contextClassLoader = cl;
}
判断当前线程是否持有某个对象的锁。
Thread.java中的相关代码:
public static native boolean holdsLock(Object obj);
获取线程ID。
Thread.java中的相关代码:
public long getId() {
return tid;
}
获取当前线程的状态。
该方法用于监控系统状态,而不是用于同步控制。
Thread.java中的相关代码:
public State getState() {
return sun.misc.VM.toThreadState(threadStatus);
}
该方法用于提示线程调度程序,当前线程愿意放弃当前处理器的使用。
该方法用来缓解线程对CPU的过度使用。
Thread.java中的相关代码:
public static native void yield();
当前正在执行的线程,在指定的时间内暂停执行。
线程不释放持有的锁。
Thread.java中的相关代码:
public static void sleep(long millis, int nanos)
throws InterruptedException {
// 若毫秒小于0,则抛出异常
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 若纳秒小于0或大于999999,则抛出异常
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
// 若纳秒超过了500000,则化零为整,毫秒加一
// 若纳秒不为零且毫秒为0,则设置最小时间1毫秒
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
// 调用重载方法
sleep(millis);
}
调用重载的sleep方法。
Thread.java中的相关代码:
public static native void sleep(long millis) throws InterruptedException;
用于强制停止线程的执行。
弃用原因:线程在被停止时,停止的线程会突然释放所有持有的锁。之前等待这些锁的对象,可能会产生不确定的行为。
Thread.java中的相关代码:
// 该方法已被弃用,不建议使用
@Deprecated
public final void stop() {
// 获取安全管理器
SecurityManager security = System.getSecurityManager();
// 若开启了安全管理器
if (security != null) {
// 检查权限,是否允许访问
checkAccess();
// 若在一个线程中停止另一个线程
if (this != Thread.currentThread()) {
// 检查权限
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// 若线程的状态不是新创建未运行的状态
if (threadStatus != 0) {
// 若线程被挂起,唤醒线程
resume();
}
// 停止线程运行
// 详解在1)处
stop0(new ThreadDeath());
}
停止线程的核心方法。
Thread.java中的相关代码:
private native void stop0(Object o);
暂停线程的执行,即将线程挂起。
若线程是活跃的,则将线程被挂起,直到线程被恢复,否则不会继续执行。
弃用原因:容易造成死锁。当线程被挂起时持有系统关键资源的锁,则在它被恢复之前,其它线程不能访问该资源。
Thread.java中的相关代码:
// 该方法已被弃用,不建议使用
@Deprecated
public final void suspend() {
// 检查权限,是否允许访问
checkAccess();
// 挂起线程
// 详解在1)处
suspend0();
}
线程挂起的核心方法。
Thread.java中的相关代码:
private native void suspend0();
恢复暂停(挂起)的线程。
弃用原因:此方法需要和suspend方法配合使用,因为suspend方法易造成死锁,被弃用,所以该方法也被弃用。
Thread.java中的相关代码:
// 该方法已被弃用,不建议使用
@Deprecated
public final void resume() {
// 检查权限,是否允许访问
checkAccess();
// 恢复线程
// 详解在1)处
resume0();
}
恢复挂起线程的核心方法。
Thread.java中的相关代码:
private native void resume0();