在日常开发中,你是否曾遇到这样的情景:应用程序需要执行多个任务,但你希望它们能够同时运行,以提高性能和响应性。这正是多线程编程的核心价值所在。
在本文中,我们将学习 Java 多线程编程的基础知识,包括线程的创建、使用、生命周期以及线程安全产生的原因。
一、线程创建与启动
线程是轻量级的,共享相同的进程内存空间。在 Java 中,线程可以使用 java.lang.Thread 类来创建和管理。
1. 实现 Runnable 接口
public class ThreadRunnableTest {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.setName("测试线程");
thread.start();
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println("线程运行,线程名称为:" + Thread.currentThread().getName());
}
}
}
调用 start 方法后,主线程会开启一条子线程去执行任务,主线程继续向下执行,两者并行执行。
2. 实现 Callable 接口(有返回值)
public class ThreadCallableTest {
public static void main(String[] args) throws Exception {
FutureTask<String> stringFutureTask = new FutureTask<>(new TaskReturn());
Thread thread = new Thread(stringFutureTask);
thread.setName("测试线程");
thread.start();
System.out.println(stringFutureTask.get());
}
private static class TaskReturn implements Callable<String> {
@Override
public String call() {
return String.format("我被线程【%s】执行了", Thread.currentThread().getName());
}
}
}
Callable 无法直接传递到 Thread 中,需要使用 FutureTask 来包装。FutureTask 的 get 方法可以获取异步任务的执行结果。
主线程与子线程:点击运行后,执行 main 方法的线程称为主线程,从 main 方法中创建的线程称为子线程。
二、线程的主要参数与 API
1. 优先级
线程的优先级是一个整数值(1-10),1 表示最低优先级,10 表示最高优先级。
thread.setPriority(Thread.MAX_PRIORITY); // 设置为最高优先级
注意:线程优先级依赖于底层操作系统支持,不保证严格按照优先级顺序执行,且过度依赖可能导致不可预测的结果。
常见问题:
- 优先级反转:低优先级线程持有锁后,高优先级线程等待时被阻塞
- 饥饿:高优先级线程长期占用资源,低优先级线程无法获得执行机会
2. 线程名称
thread.setName("测试线程"); // 设置线程名称
Thread.currentThread().getName() // 获取当前线程名称
线程名称在排查问题(如死锁、系统变慢)时非常重要,可以使用 jconsole、jstack 等 JVM 工具监控。
3. 守护线程
守护线程的生命周期随主线程结束而终止,用于执行后台任务(如垃圾回收、定时任务、监控等)。
Thread thread = new Thread(new Task());
thread.setDaemon(true); // 设置为守护线程
thread.start();
守护线程 vs 工作线程:
- 守护线程:主线程执行完毕,守护线程立即停止
- 工作线程:JVM 会等待所有工作线程结束后才停止
注意:守护线程可能来不及执行资源回收(如关闭 JDBC 连接),需谨慎使用。
4. 停止线程
不推荐使用 thread.stop(),会导致资源不释放。
推荐使用 interrupt 方法请求线程停止:
public class StopThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Task());
thread.setName("测试线程");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); // 发出停止信号
}
private static class Task implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("我执行了");
}
}
}
}
interrupt 方法会在线程等待条件时触发 InterruptedException 异常,需配合循环检测中断信号来终止线程。
三、线程生命周期和状态
Java 线程的生命周期包括以下状态:
| New | 线程对象已创建,但尚未启动 |
| Runnable | 线程已准备好运行,等待 CPU 时间片 |
| Running | 线程获得 CPU 时间片并开始执行 |
| Blocked | 线程被阻塞,等待锁或其他条件 |
| Waiting | 线程等待其他线程通知继续执行 |
| Timed Waiting | 线程等待一段时间后自动唤醒 |
| Terminated | 线程生命周期结束 |
状态转换图:
#mermaid-svg-Gd7zbkcjVyeSU9ew{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Gd7zbkcjVyeSU9ew .error-icon{fill:#552222;}#mermaid-svg-Gd7zbkcjVyeSU9ew .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Gd7zbkcjVyeSU9ew .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .marker.cross{stroke:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Gd7zbkcjVyeSU9ew p{margin:0;}#mermaid-svg-Gd7zbkcjVyeSU9ew defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Gd7zbkcjVyeSU9ew g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Gd7zbkcjVyeSU9ew g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Gd7zbkcjVyeSU9ew g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Gd7zbkcjVyeSU9ew g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Gd7zbkcjVyeSU9ew .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Gd7zbkcjVyeSU9ew .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Gd7zbkcjVyeSU9ew .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Gd7zbkcjVyeSU9ew .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Gd7zbkcjVyeSU9ew .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Gd7zbkcjVyeSU9ew .edgeLabel .label text{fill:#333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .label div .edgeLabel{color:#333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Gd7zbkcjVyeSU9ew .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Gd7zbkcjVyeSU9ew .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Gd7zbkcjVyeSU9ew .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .cluster-label,#mermaid-svg-Gd7zbkcjVyeSU9ew .nodeLabel{color:#131300;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .note-edge{stroke-dasharray:5;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-note text{fill:black;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagram .edgeLabel{color:red;}#mermaid-svg-Gd7zbkcjVyeSU9ew #dependencyStart,#mermaid-svg-Gd7zbkcjVyeSU9ew #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Gd7zbkcjVyeSU9ew .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Gd7zbkcjVyeSU9ew :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
创建线程对象
start()
获得CPU时间片
等待锁
wait()
sleep()
获得锁
notify/notifyAll
时间到或notify
任务执行完毕
New
Runnable
Running
Blocked
Waiting
Timed_Waiting
Terminated
四、竞态条件和临界区
并发安全问题的产生源于多线程同时访问共享资源,且操作顺序敏感。
小故事:面包店的小明雇佣了两个助手小红和小绿做蛋糕。第三步"添加奶油和装饰"需要按顺序完成,但由于两人同时操作,导致蛋糕做砸了。
解决方案:引入规则——同时只有一人能操作最后一步。这个规则就是锁!
关键概念:
- 临界区:共享资源或代码段,多线程同时访问会产生问题
- 竞态条件:多个线程同时以不同顺序访问临界区
- 锁:保证同一时刻只有一个线程能访问临界区
五、总结
线程是提升服务器资源利用率的关键工具。在本节中,我们学习了:
- 线程的创建方式(Runnable、Callable)
- 线程的参数(优先级、名称、守护线程)
- 线程的停止方法(interrupt)
- 线程的生命周期和状态
- 并发安全问题的产生原因(竞态条件、临界区)
下一节,我们将学习如何合理、高效、安全地使用线程。
网硕互联帮助中心




评论前必须登录!
注册