目录
[新建(NEW) ➡️ 就绪(RUNNABLE)](#新建(NEW) ➡️ 就绪(RUNNABLE))
[就绪(RUNNABLE) ➡️ 就绪(RUNNABLE)](#就绪(RUNNABLE) ➡️ 就绪(RUNNABLE))
[就绪(RUNNABLE) ➡️ 阻塞(BLOCKED)](#就绪(RUNNABLE) ➡️ 阻塞(BLOCKED))
[阻塞(BLOCKED) ➡️ 就绪(RUNNABLE)](#阻塞(BLOCKED) ➡️ 就绪(RUNNABLE))
[就绪(RUNNABLE) ➡️ 等待(WAITING)](#就绪(RUNNABLE) ➡️ 等待(WAITING))
[等待(WAITING) ➡️ 就绪(RUNNABLE)](#等待(WAITING) ➡️ 就绪(RUNNABLE))
[就绪(RUNNABLE) ➡️ 定时等待(TIMED_WAITING)](#就绪(RUNNABLE) ➡️ 定时等待(TIMED_WAITING))
[定时等待(TIMED_WAITING) ➡️ 就绪(RUNNABLE)](#定时等待(TIMED_WAITING) ➡️ 就绪(RUNNABLE))
[就绪(RUNNABLE) ➡️ 终止状态(TERMINATED)](#就绪(RUNNABLE) ➡️ 终止状态(TERMINATED))
Thread类上的注释
注释一
- 线程是程序中的执行线程
- Java虚拟机允许应用程序同时运行多个执行线程
java
package java.lang;
//A thread is a thread of execution in a program.
//The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
public
class Thread implements Runnable {
}
注释二
- 每一个线程都有优先级
- 优先级高的线程会优于优先级低的线程执行(具体不一定,要看实际情况)
java
package java.lang;
//Every thread has a priority.
//Threads with higher priority are executed in preference to threads with lower priority.
public
class Thread implements Runnable {
}
注释三
- 每个线程可以被设置成守护线程,也可以不设置成守护线程
- 在线程的运行过程中创建了新的线程对象,这个新线程会有默认优先级
- 可以在创建线程对象后,执行线程对象的start方法之前设置该线程为守护线程
java
package java.lang;
//Each thread may or may not also be marked as a daemon.
//When code running in some thread creates a new Thread 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.
public
class Thread implements Runnable {
}
注释四
虚拟机启动时会创建一个名为main的非守护线程,并执行该线程直到遇到如下情况
- 调用Runtime的exit方法
- 所有非守护线程都已经终止
java
package java.lang;
//When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class).
//The Java Virtual Machine continues to execute threads until either of the following occurs:
//1、The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
//2、All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
public
class Thread implements Runnable {
}
注释五
有两种方法可以创建新的执行线程
- 定义一个类作为Thread类的子类,子类应该去重写Thread类的run方法
- 定义一个实现了Runnable接口的类,该类实现Runnable提供的run方法
java
package java.lang;
//There are two ways to create a new thread of execution.
//One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread.
//The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method.
public
class Thread implements Runnable {
}
注释六
- 每一个线程都有一个用于标识的名字
- 多个线程可以具有相同的名字
- 如果在创建线程时未指定名字,会为其创建一个默认名字(默认有生成名字的算法)
java
package java.lang;
//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.
public
class Thread implements Runnable {
}
查看Java虚拟机启动后创建的线程信息
运行Java程序
java
package concurrency;
/**
* 1、启动一个Java程序(一个Java进程)
* 2、使用【jps】查看该Java进程的ID
* 3、使用【jconsole + 进程ID】查看线程信息
*/
public class ShowThreadTest {
public static void main(String[] args) {
// sleep 100s:这里仅仅是为了让该Java程序不那么快退出,方便查看该Java进程中的线程信息
sleepMethod(100_1000);
}
public static void sleepMethod(long time) {
if (time <= 0) return;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
使用【jconsole】查看Java进程中的线程信息


Thread类中的部分属性
java
package java.lang;
//...
public
class Thread implements Runnable {
// ...
// 线程名字
private volatile String name;
// 线程优先级
private int priority;
// 是否为守护线程
private boolean daemon = false;
// 可以通过构造器传入一个Runnable类型的对象
private Runnable target;
// 线程组(默认和父线程为同一个线程组)
private ThreadGroup group;
// 用于默认生成线程的名字
private static int threadInitNumber;
// 线程栈的大小(虚拟机栈是线程私有的内存区域,每个线程都有)
private long stackSize;
// 线程ID
private long tid;
// 用于生成线程ID
private static long threadSeqNumber;
// 线程状态
private volatile int threadStatus = 0;
// 线程最小优先级
public final static int MIN_PRIORITY = 1;
// 线程默认优先级
public final static int NORM_PRIORITY = 5;
// 线程最大优先级
public final static int MAX_PRIORITY = 10;
// ...
}
Thread类中的构造器
构造器的几种重载形式
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
// 无参构造器,所有参数都使用默认值
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 指定任务执行逻辑,这样的好处是可以让执行逻辑和线程的控制分离
// 有点像策略模式,这里的target中有具体的执行策略
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
// 指定线程组 + 执行逻辑
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
// 指定线程名字
public Thread(String name) {
init(null, null, name, 0);
}
// 指定线程组 + 线程名字
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
// 指定任务执行逻辑 + 线程名字
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
// 指定线程组 + 任务执行逻辑
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
// 指定线程组 + 任务执行逻辑 + 线程名字
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
// ...
}
init方法
通过上述源码可以看到,构造器中调用了init方法,下面来看init方法
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
// 可以指定如下几个参数
// 1、线程组
// 2、业务执行逻辑单元
// 3、线程名字
// 4、线程私有栈的大小
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// ...
// 线程名字
this.name = name;
// 获取父线程对象
// 比方在main线程中创建一个线程对象,那么这里获取到的就是main线程
Thread parent = currentThread();
// 此处省略一些和安全相关的源代码
// ...
if (g == null) {
// ...
if (g == null) {
// 默认和父线程为同一个线程组
g = parent.getThreadGroup();
}
}
// ...
// 设置线程组
this.group = g;
// 设置是否为守护线程,如果父线程是守护线程,这里就是守护线程
this.daemon = parent.isDaemon();
// 默认优先级和父线程优先级一致
this.priority = parent.getPriority();
// ...
// 设置业务执行单元
this.target = target;
// 设置线程优先级
setPriority(priority);
// ...
// 栈大小
this.stackSize = stackSize;
// 设置线程ID
/* Set thread ID */
tid = nextThreadID();
}
// ...
}
线程的状态
操作系统中的线程状态
在操作系统中,线程一般包括如下几种状态
- 新建:线程尚未分配到系统资源,未参与调度
- 就绪:线程已经准备好运行,等待CPU时间片,已进入可调度队列
- 运行:线程获取到CPU执行权正在CPU上运行,多核系统中,多个线程可同时处于运行状态
- 阻塞:线程因等待某个条件而暂停运行(如:IO操作、获取锁、信号量等),线程进入阻塞状态会放弃CPU执行权,即时有CPU空闲也不会被调度
- 等待:线程进入无限期等待,直到被其他线程唤醒(如notify或singal),等待状态下的线程会放弃CPU的执行权
- 超时等待:线程在指定时间内等待,直到时间到或者在等待期间被其他线程唤醒
- 终止:线程已经执行完毕或者因为异常而退出,不可再被调度
Java中的线程状态
在Java中,线程的状态定义在Thread类中的一个枚举中,这里看一下源码
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
// 这里的状态和操作系统中线程状态相比少了一个运行状态
// 猜想Java中没有定义线程的运行状态的原因可能是因为此时线程已经交给操作系统了
public enum State {
// 新建状态
NEW,
// 就绪状态
RUNNABLE,
// 阻塞状态
BLOCKED,
// 等待状态
WAITING,
// 定时等待状态
TIMED_WAITING,
// 终止状态
TERMINATED;
}
// ...
}
Java线程状态之间的切换
新建(NEW) ➡️ 就绪(RUNNABLE)
- 线程对象调用了start方法
- 等待CPU调度,等待获取CPU执行权
就绪(RUNNABLE) ➡️ 就绪(RUNNABLE)
- 本质上来看是就绪状态切换到操作系统级别的运行状态
- 线程交给操作系统调度,获取到了CPU的执行权
就绪(RUNNABLE) ➡️ 阻塞(BLOCKED)
- 线程尝试进入synchronized代码块,但锁被其他线程持有
阻塞(BLOCKED) ➡️ 就绪(RUNNABLE)
- 一旦锁被其他线程释放,该线程从阻塞状态变为就绪状态准备竞争锁
就绪(RUNNABLE) ➡️ 等待(WAITING)
- 调用Object.wait(),需要先获取到对象的锁,调用后释放锁进入等待状态
- 调用Thread.join(),会等待目标线程终止
- 调用LockSupport.park()
等待(WAITING) ➡️ 就绪(RUNNABLE)
- 调用Object.notify() 唤醒,被唤醒后的线程需要重新竞争锁资源
- 调用Object.notifyAll() 唤醒,被唤醒后的线程需要重新竞争锁资源
就绪(RUNNABLE) ➡️ 定时等待(TIMED_WAITING)
- 调用Thread.sleep(timeout)
- 调用Object.wait(timeout)
- 调用Thread.join(timeout)
- 调用LockSupport.parkNanos(nanos)
定时等待(TIMED_WAITING) ➡️ 就绪(RUNNABLE)
- 超时时间到自动唤醒
- 超时时间未到但被Object.notify() 或Object.notifyAll() 唤醒,唤醒后的线程需要重新竞争锁资源
- 超时时间未到被中断
就绪(RUNNABLE) ➡️ 终止状态(TERMINATED)
- 线程执行完run() 中的代码
- 因异常而退出
Thread类中的部分方法
start()方法
查看源码
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
// 线程默认状态为0,未启动
private volatile int threadStatus = 0;
public synchronized void start() {
// 线程状态不为0,抛异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
boolean started = false;
try {
// 调用start0()方法
start0();
started = true;
} finally {
// ...
}
}
private native void start0();
// ...
}
源码分析
- 调用两次线程对象的start()方法,会报错
- start()方法本质上执行的是native修饰的start0()方法,交给操作系统来调度
- start0()方法的具体实现是C语言,会调用到线程的run()方法来执行run()中的逻辑
run()方法
查看源码
java
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
@Override
public void run() {
if (target != null) {
target.run();
}
}
// ...
}
源码分析
- Thread类中的run()方法是对函数式接口Runnable接口中提供的抽象方法的实现
- 若在创建线程对象时传入了Runnable类型的对象,最终执行的是传入的Runnable类型对象的run()方法
- 若使用Thread子类来创建对象,最终会调用到该子类中重写的run()方法中的逻辑
join()方法
查看源码
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
// ...
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
// 调用join()方法时没有超时时间吗,一致等待目标线程执行完
while (isAlive()) {
wait(0);
}
} else {
// 调用join(timeout)指定了超时时间,超时不等待
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
// ...
}
分析源码
- Thread类中的join方法本质上是调用了Object.wait方法