与 Java 线程的邂逅
摘要 本文介绍了操作系统中的进程与线程概念,重点阐述了线程的特性与管理。主要内容包括:进程是资源分配的最小单元,线程是CPU调度的最小单元;线程优先级决定处理器资源分配;Java线程的6种状态(NEW、RUNNABLE、BLOCKED等)及其转换;守护线程的特点;线程的启动通过start()方法实现,而中断应采用interrupt()配合状态检查的安全方式,避免使用废弃的suspend()/sto
1. 进程与线程
现代操作系统在运行一个程序时,会为其创建一个进程。在一个进程里可以创建多个线程,这些线程有自己独立的计数器,栈空间,并且能够访问进程的共享内存。
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是操作系统分配资源的的最小单元。
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程是操作系统调度的最小单元。
为什么要使用多线程?
- 更多的处理器核心:更好的利用好多核处理器。
- 更短的响应时间。
- 更好的变成模型。
2. 线程的优先级
现代操作系统采用 分时调度,操作系统会分出一个个时间片,线程会被分配若干个时间片。当线程的时间片用完了就会发生线程调度,并等待下次分配。线程被分配到时间片的多少决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要分配多少处理器资源的线程属性。
Thread thread = new Thread();
// 默认是 5
thread.setPriority(7);
3. 线程的状态
| 状态名称 | 说明 |
|---|---|
| NEW | 初始状态 线程被创建 但是还没有调用 start() 方法 |
| RUNNABLE | 运行状态 Java线程讲操作系统的就绪和运行状态笼统地称为 运行中 |
| BLOCKED | 阻塞状态 表示线程阻塞于锁 |
| WAITNG | 等待状态 表示线程进入等待状态 进入该状态表示当前线程需要等待其他线程的通知或中断 |
| TIME_WATING | 超时等待状态 不同于上面,可以在指定的时间自行返回 |
| TERMINATED | 终止状态 表示当前线程已经执行完毕 |
3.1 线程状态枚举类
Thread.State
public enum State {
/**
● Thread state for a thread which has not yet started.
*/
NEW,(新建)
/**
● Thread state for a runnable thread. A thread in the runnable
● state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,(准备就绪)
/**
● Thread state for a thread blocked waiting for a monitor lock.
● A thread in the blocked state is waiting for a monitor lock
● to enter a synchronized block/method or
● reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,(阻塞)
/**
● Thread state for a waiting thread.
● A thread is in the waiting state due to calling one of the
* following methods:
● <ul>
● <li>{@link Object#wait() Object.wait} with no timeout</li>
● <li>{@link #join() Thread.join} with no timeout</li>
● <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
● <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
● For example, a thread that has called <tt>Object.wait()</tt>
● on an object is waiting for another thread to call
● <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,(等待)
/**
● Thread state for a waiting thread with a specified waiting time.
● A thread is in the timed waiting state due to calling one of
● the following methods with a specified positive waiting time:
● <ul>
● <li>{@link #sleep Thread.sleep}</li>
● <li>{@link Object#wait(long) Object.wait} with timeout</li>
● <li>{@link #join(long) Thread.join} with timeout</li>
● <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
● <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
● </ul>
*/
TIMED_WAITING,(等待)
/**
● Thread state for a terminated thread.
● The thread has completed execution.
*/
TERMINATED;(终结)
}
3.2 线程状态变迁

lock 锁下的 condition 调用 await() 方法也会进入等待状态。
阻塞状态是线程阻塞在 synchronized 关键字修饰的方法或者方法块时的状态,但是阻塞在 java.concurrent 包中 Lock 接口的线程状态是等待状态,因为 java.concurrent 包中 Lock 接口对于阻塞的实现使用了 LockSupport 类中的相关方法。
3.3 wait/sleep 的区别
- sleep 是 Thread 的静态方法,wait 是 Object 的方法,任何对象实例都能调用。
- sleep 不会释放锁,它也不需要占用锁。wait 会释放锁,但调用它的前提是当前线程占有锁(即代码要在 synchronized 中)。
- 它们都可以被 interrupted 方法中断。
//todo join方法呢?
4. Deamon 线程
Deamon 线程是一个支持型的线程(守护线程),主要作用是程序中的后台调度以及支持工作。
当一个 Java 虚拟机中不存在 非 Deamon 线程的时候,Java虚拟机将退出。
Thread thread = new Thread();
thread.setDaemon(true);
Deamon 线程中的 finally 块不一定能够执行到。
5. 启动和终止线程
构造线程
线程对象(Thread)的构造过程涉及一个关键的底层方法 init(),它负责线程的底层初始化工作。这个方法是线程对象构造的核心,但开发者通常不会直接调用它,因为它被封装在 Thread 的构造函数中。
init() 是 Thread 类中的一个 私有方法,所有 Thread 的构造函数最终都会调用它来完成以下任务:
● 初始化线程属性(名称、线程组、优先级、守护线程标志等)。
● 绑定线程任务(Runnable 对象)。
● 设置线程堆栈大小(通过 stackSize 参数)。
● 与 JVM 本地线程建立关联(通过本地方法 registerNatives())。
5.1 启动线程
在线程对象初始化完成后,调用 start() 方法就可以启动线程了。start() 方法的含义是:当前线程(即父线程)同步告知 Java 虚拟机,只要线程规划起空闲,就应立即启动调用 start() 方法 的线程。
5.2 中断线程
Thread 线程提供了 暂停/恢复/停止的 方法:suspend() / resume() / stop() 。但是这三个方法都被 标记为废弃(@Deprecated),因为其设计存在严重的安全性和可靠性问题。比如当调用上述方法暂停或者停止线程时,线程持有的资源并不会释放,容易造成死锁等问题。停止线程会强制停止线程,会使得很多资源来不及释放。
| 方法 | 废弃原因 |
|---|---|
| suspend() | 导致死锁,不释放锁,破坏线程安全。 |
| resume() | 依赖 suspend(),无法解决根本问题。 |
| stop() | 强制终止导致数据不一致和资源泄漏,违背“协作式线程管理”原则。 |
5.3 如何中断/停止线程
理解中断:中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。其他线程通过调用该线程的 interrupt() 方法进行中断操作。
线程通过检查自身是否被中断来进行响应,可以通过 isInterrupted() 来判断是否被中断,也可以调用静态方法 Thread.interrupted() 对当前线程的中断标识位进行复位。

安全的终止线程演示:
public class GracefulThreadStopDemo {
static class StoppableTask implements Runnable {
// 自定义终止标志(volatile 保证可见性)
private volatile boolean stopRequested = false;
// 外部调用此方法请求终止
public void requestStop() {
stopRequested = true;
}
@Override
public void run() {
try {
// 循环检查终止条件:自定义标志 + 中断标志
while (!stopRequested && !Thread.currentThread().isInterrupted()) {
// 模拟工作任务(包含可能阻塞的操作)
doWork();
}
} finally {
// 终止前的资源清理
cleanup();
}
}
private void doWork() {
try {
System.out.println(Thread.currentThread().getName() + ": Working...");
// 模拟耗时操作(可被中断的阻塞操作)
Thread.sleep(1000);
} catch (InterruptedException e) {
// 捕获中断异常,恢复中断标志,并退出循环
System.out.println(Thread.currentThread().getName() + ": Interrupted!");
Thread.currentThread().interrupt(); // 重新设置中断标志
}
}
private void cleanup() {
System.out.println(Thread.currentThread().getName() + ": Cleaning up resources...");
}
}
public static void main(String[] args) throws InterruptedException {
StoppableTask task = new StoppableTask();
Thread workerThread = new Thread(task, "WorkerThread");
workerThread.start();
// 主线程等待 3 秒后请求终止
Thread.sleep(3000);
task.requestStop(); // 通过自定义标志请求终止
// 可选:同时发送中断信号(双重保险)
// workerThread.interrupt();
// 等待工作线程结束
workerThread.join();
System.out.println("Main: Worker thread terminated.");
}
}
上面的代码有双重终止条件
while (!stopRequested && !Thread.currentThread().isInterrupted()) {
// 执行任务...
}
● stopRequested:通过 volatile 保证多线程可见性,外部调用 requestStop() 设置此标志。
● Thread.currentThread().isInterrupted():检查线程的中断标志位,响应 interrupt() 请求。
6. 线程间通信
Java支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程可以拥有这个变量的副本,所以在程序执行过程中,一个线程看到的变量并不一定是最新的。
6.1 volatile 和 synchronized机制
关键字 volatile 可以用来修饰一个成员变量,告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存。它能保证所有的线程对变量访问的可见效。
关键字 synchronized 可以修饰方法或者同步块,它主要起确保在同一个时刻只能用一个线程处于方法或者同步块中,从而保证了线程对变量访问的可见效和排他性。
6.2 等待通知机制
Java 提供了内置的等待/通知机制。
等待/通知机制是指一个 线程A 调用了对象O 的 wait() 方法进入等待状态,而另一个 线程B 调用了 对象O 的notify() 方法或者 notifyAll() 方法,线程A 收到通知后从 对象O 的等待状态返回,进而执行后序操作。
上述两个线程通过那 对象O 来完成交互,而对象上的 wait() / notify() / notifyAll() 就如同开关信号一样,用来完成等待方和通知方之间交互工作。
注意:
- 调用上述方法之前需要先获取来锁。
- 调用完 wait() 方法后,线程状态由运行状态转变为等待状态,并将当前线程放置到对象的等待队列。
6.3 thread.join()
当 线程A 执行了 线程B 的 join() 方法后,线程A 等待 线程B 终止之后才能从 该处返回。
public static void main(String[] args) {
Thread threadB = new Thread(
new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
}
}, "threadB"
);
Thread threadA = new Thread(
new Runnable() {
@SneakyThrows
@Override
public void run() {
// 插队的效果
threadB.join();
System.out.println(Thread.currentThread().getName());
}
}, "threadA"
);
threadA.start();
threadB.start();
}
执行结果如下:
threadB
threadA
6.4ThreadLocal
7. 并发与并行
7.1串行模式
串行表示所有任务都一一按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴,并且只有完成了这整个三个步骤,才能进行下一个步骤。串行是一次只能取得一个任务,并执行这个任务。
7.2 并行模式
并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核 CPU。
7.3 并发
并发(concurrent)指的是多个程序可以同时运行的现象,更细化的是多进程可以同时运行或者多指令可以同时运行。但这不是重点,在描述并发的时候也不会去扣这种字眼是否精确,并发的重点在于它是一种现象, 并发描述的是多进程同时运行的现象。但实际上,对于单核心 CPU 来说,同一时刻只能运行一个线程。所以,这里的"同时运行"表示的不是真的同一时刻有多个线程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同时运行起来了,但实际上这些程序中的进程不是一直霸占 CPU 的,而是执行一会停一会。 (宏观并行)
要解决大并发问题,通常是将大任务分解成多个小任务, 由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行。这可能会出现一些现象:
● 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果
● 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务。
● 可以多进程/多线程的方式并行执行这些小任务。也可以单进程/单线程执行这些小任务,这时很可能要配合多路复用才能达到较高的效率
小结(重点)
并发:同一时刻多个线程在访问同一个资源,多个线程对一个点
并行:同一时刻 多项工作一起执行
8. 管程
管程(monitor)是保证了同一时刻只有一个进程在管程内活动, 即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现). 但是这样并不能保证进程以设计的顺序执行
JVM 中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程 (monitor)对象,管程(monitor) 会随着 Java 对象一同创建和销毁
执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方法在执行时候会持有管程,其他线程无法再获取同一个管程.
更多推荐

所有评论(0)