线程的关键属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 线程名称
private volatile String name;
// 声明线程的优先级,具有较高优先级的线程优先于具有较低优先级的线程执行。但是,线程优先级的设定并不是强制性的,也就是说,具有较低优先级的线程并不总是等待具有较高优先级的线程执行完毕后才执行。具体取决与操作系统。
private int priority;
// 声明线程是否时守护线程, 在Java中,线程分为用户线程和守护线程两种。用户线程是指用户通过代码创建的普通线程,而守护线程则是一种特殊的线程,主要用于在后台提供通用服务,如垃圾回收线程。
private boolean daemon = false;
// 声明线程是否被中断
private volatile boolean interrupted;
// 用于控制和管理线程的生命周期。为true,表示线程在启动之前就已经结束了;如果值为false,那么表示线程在启动之后仍然在运行。
private boolean stillborn = false;
// 供JVM调用,当线程启动时,eetop被设置为非零值,当线程终止时,eetop被重置为零。因此,一个非零的值表示线程是活动的
private volatile long eetop;
// 代表线程需要执行的任务
private Runnable target;
// 线程所在的线程组
private ThreadGroup group;
// 用户生成新线程的名称:"Thread-" + nextThreadNum()
private static int threadInitNumber;
// 用于存储当前线程的局部变量。每个线程都有自己的threadLocals,这样每个线程可以独立地存储和管理自己的线程局部变量。
ThreadLocal.ThreadLocalMap threadLocals = null;
// 指定线程的堆栈大小
private final long stackSize;
// 线程编号,线程的唯一标识符
private final long tid;
// 用户生成线程tid
private static long threadSeqNumber;
// 声明线程的状态
private volatile int threadStatus;
// 线程的最小优先级
public static final int MIN_PRIORITY = 1;
// 线程的默认优先级
public static final int NORM_PRIORITY = 5;
// 线程的最大优先级
public static final int MAX_PRIORITY = 10;

线程的状态以及流转方式

线程的状态主要有以下几种:

  • NEW

    这个状态表示线程已经创建但尚未启动。线程在创建后,调用start()方法之前处于这个状态。

  • RUNNABLE

    这个状态表示线程正在运行或准备运行,并且正在等待系统分配资源。

  • BLOCKED

    这个状态表示线程正在等待监视器锁以进入或重新进入一个同步块/方法。这个状态表示线程被阻塞并等待另一个线程释放锁。

    有两种情况会进入这个状态:

    • 当一个线程试图进入一个synchronized块,而该块的锁已经被其他线程持有。
    • 当一个线程在一个对象上调用wait方法后,另一个线程调用了该对象的notify或notifyAll方法,但是原线程无法立即获取到该对象的锁。
  • WAITING

    这个状态表示线程正在无限期地等待另一个线程执行特定的操作。当线程调用如Object.wait()(无超时)、Thread.join()(无超时)或LockSupport.park()等方法时,会进入这个状态。

  • TIMED_WAITING

    这个状态表示线程正在等待另一个线程执行操作,但最多等待指定的时间。当线程调用如Thread.sleep()、Object.wait(long timeout)、Thread.join(long millis)、LockSupport.parkNanos()或LockSupport.parkUntil()等方法时,会进入这个状态。

  • TERMINATED

    这个状态表示线程已经完成其执行并且不再活动。

流转过程可以参考这个图:

image-20240322150459920

线程的构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 构造一个默认熟悉感的线程
public Thread() {
this(null, null, "Thread-" + nextThreadNum(), 0);
}
// 接收一个Runnable对象作为参数。新线程开始运行时,将调用Runnable对象的run方法。
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
// 接收一个ThreadGroup对象和一个Runnable对象作为参数。新线程将成为指定的线程组的成员,并在开始运行时调用Runnable对象的run方法。
public Thread(ThreadGroup group, Runnable target) {
this(group, target, "Thread-" + nextThreadNum(), 0);
}
// 构造一个指定了线程名的线程
public Thread(String name) {
this(null, null, name, 0);
}
// 接收一个ThreadGroup对象和一个字符串作为参数。新线程将成为指定的线程组的成员,其名称将被设置为提供的字符串。
public Thread(ThreadGroup group, String name) {
this(group, null, name, 0);
}
// 接收一个Runnable对象和一个字符串作为参数。新线程开始运行时,将调用Runnable对象的run方法,其名称将被设置为提供的字符串。
public Thread(Runnable target, String name) {
this(null, target, name, 0);
}
// 接收一个ThreadGroup对象、一个Runnable对象和一个字符串作为参数。新线程将成为指定的线程组的成员,在开始运行时调用Runnable对象的run方法,其名称将被设置为提供的字符串。
public Thread(ThreadGroup group, Runnable target, String name) {
this(group, target, name, 0);
}
// 接收一个ThreadGroup对象、一个Runnable对象和一个字符串以及堆栈大小作为参数。
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
// 创建一个新的Thread对象,具有特定的ThreadGroup,一个Runnable目标,一个名称,一个堆栈大小,以及一个布尔值来确定是否继承线程局部变量。
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize, boolean inheritThreadLocals) {
this(group, target, name, stackSize, null, inheritThreadLocals);
}

这些构造器底层都会调用一个私有构造器去初始化线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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;

Thread parent = currentThread();
// 拿到线程安全管理器
SecurityManager security = System.getSecurityManager();
if (g == null) { // 如果线程组为null,就要拿到线程组

if (security != null) { // 如果有线程安全管理器,则从线程安全管理中获取线程组
g = security.getThreadGroup();
}

if (g == null) { // 如果安全管理器不提供ThreadGroup,使用父线程的ThreadGroup
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);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
// 是否继承父线程的局部变量
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

this.stackSize = stackSize;

this.tid = nextThreadID();
}

线程的创建方式

  1. 继承Thread类:创建一个新的类,该类继承自Thread类,然后重写Thread类的run()方法。然后可以创建该类的实例,并调用其start()方法来启动新线程。

  2. 实现Runnable接口:创建一个新的类,该类实现Runnable接口,然后实现Runnable接口的run()方法。然后可以创建该类的实例,并将其作为参数传递给Thread类的构造函数,然后调用Thread对象的start()方法来启动新线程.

    总的来说,实现Runnable接口的方式更为常用,因为它允许你的类可以继承其他类。

线程的关键方法

start()

用于启动一个线程,同一个线程只能调用一次start(),否则会抛出IllegalThreadStateException异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public synchronized void start() {
// 如果线程状态不是NEW,抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();

// 将当前线程添加到线程组中,同时线程组中的未启动线程数量也要减一
group.add(this);

boolean started = false;
try {
// 调用原生方法去执行线程
start0();
started = true;
} finally {
try {
if (!started) { // 如果启动失败,则将线程状态回滚,权当线程没执行。等待后续调度执行。
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}

private native void start0();

中断操作

interrupt()

线程的中断操作是一种通知机制,用于告诉线程应该停止当前的工作并做其他事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void interrupt() {
if (this != Thread.currentThread()) {
// 如果当前线程不允许修改它试图中断的线程,可能会抛出SecurityException。
checkAccess();

// thread may be blocked in an I/O operation
synchronized (blockerLock) { // 检查线程是否在I/O操作中被阻塞
Interruptible b = blocker;
if (b != null) { // 是的话,将中断状态置为TRUE,并中断这个IO操作
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
// 设置中断标识
interrupted = true;
// 执行中断
interrupt0();
}

interrupted()

用于检查当前线程是否已被中断,并清除中断状态。

1
2
3
4
5
6
7
8
9
public static boolean interrupted() {
Thread t = currentThread();
boolean interrupted = t.interrupted;
if (interrupted) {
t.interrupted = false;
clearInterruptEvent();
}
return interrupted;
}

一个关于中断操作的小demo

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
System.out.println("Hello, World!");
});
thread.start();
thread.interrupt();
}

join

Thread类的join方法用于使当前线程等待,直到调用join方法的线程结束为止。这个方法可以用于确保一个线程在另一个线程完成其工作后再开始执行

join方法有三种形式:

  1. void join() throws InterruptedException: 这个方法会使当前线程无限期等待,直到调用join方法的线程结束。

  2. void join(long millis) throws InterruptedException: 这个方法会使当前线程等待指定的毫秒数,或者直到调用join方法的线程结束,以先到者为准。

  3. void join(long millis, int nanos) throws InterruptedException: 这个方法会使当前线程等待指定的毫秒数和纳秒数,或者直到调用join方法的线程结束,以先到者为准。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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方法,使当前线程处于等待
    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的小例子

在这个例子中,thread1内部调用了thread的join方法,意味着它会在thread线程执行完毕后再执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("Thread is finished");
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
});

Thread thread1 = new Thread(() -> {
try {
thread.join();
System.out.println("Thread 1 is waiting for thread to finish");
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
});
thread.start();
thread1.start();
}

结果为:

Thread is finished
Thread 1 is waiting for thread to finish