Java 多线程编程入门:从创建线程到线程池的使用
一、前言
线程是 Java 进阶开发的分水岭,也是后端开发、高并发场景的核心基础。很多初学者学到多线程时容易陷入生命周期、锁、线程池参数的混乱。 本文从零梳理 Java 线程完整知识链路:从 3 种线程创建方式、线程安全同步锁、死锁问题,再到线程池七大核心参数,最后讲解CompletableFuture异步编程,配套可运行代码示例,看完就能上手开发并发程序。
二、三种创建线程方式
2.1 继承 Thread 类
继承Thread并重写run()方法,调用start()启动线程(不能直接调用 run,否则只是普通方法调用)。
java
运行
public class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadDemo t1 = new ThreadDemo();
t1.setName("线程A");
t1.start();
}
}
优缺点: 优点:写法简单;缺点:Java 单继承,无法再继承其他类,扩展性差。
2.2 实现 Runnable 接口
实现Runnable接口,传入 Thread 构造器,规避单继承限制,推荐基础场景使用。
java
运行
public class RunnableDemo implements Runnable {
@Override
public void run() {
System.out.println("Runnable线程:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
RunnableDemo run = new RunnableDemo();
new Thread(run, "线程B").start();
}
}
2.3 Callable + Future 带返回值线程
前两种方式无返回值,Callable支持线程执行完返回结果,搭配FutureTask使用。
java
运行
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 10; i++) sum += i;
return sum;
}
public static void main(String[] args) throws Exception {
CallableDemo call = new CallableDemo();
FutureTask<Integer> task = new FutureTask<>(call);
new Thread(task).start();
// 获取线程返回结果
System.out.println("计算结果:" + task.get());
}
}
三、线程安全与同步锁 synchronized

3.1 线程安全问题演示
多线程共享同一变量时,会出现数据错乱(竞争资源):
java
运行
class Ticket implements Runnable{
private int ticket = 10;
@Override
public void run() {
while(ticket > 0){
System.out.println("卖出票:" + ticket--);
}
}
}
// 多线程执行会出现重复票数、负数票
3.2 synchronized 同步锁解决方案
锁分为同步方法、同步代码块,保证同一时间只有一个线程执行临界区代码。
- 同步代码块(推荐,锁粒度更小)
java
运行
@Override
public void run() {
while(true){
synchronized (this){
if(ticket <= 0) break;
System.out.println("卖出票:" + ticket--);
}
}
}
- 同步方法:锁对象为当前实例 this
java
运行
public synchronized void saleTicket(){
if(ticket > 0){
System.out.println("卖出票:" + ticket--);
}
}
核心原理:内置监视器锁,独占式互斥,保证原子性、可见性、有序性。
四、死锁演示与避免
插入位置:4.1 代码示例下方 配图描述:双向循环依赖图,线程 1 持有 LockA 等待 LockB,线程 2 持有 LockB 等待 LockA,箭头形成闭环,标注死锁四大必要条件。
4.1 死锁代码示例
两个线程互相持有对方需要的锁,无限阻塞:
java
运行
public class DeadLockDemo {
static Object lockA = new Object();
static Object lockB = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lockA){
System.out.println("线程1拿到锁A,等待锁B");
try { Thread.sleep(1000); } catch (Exception e){}
synchronized (lockB){
System.out.println("线程1执行完毕");
}
}
}).start();
new Thread(() -> {
synchronized (lockB){
System.out.println("线程2拿到锁B,等待锁A");
try { Thread.sleep(1000); } catch (Exception e){}
synchronized (lockA){
System.out.println("线程2执行完毕");
}
}
}).start();
}
}
运行后程序永久卡住,无输出结束。
4.2 死锁四大必要条件
- 互斥条件:锁同一时间只能一个线程持有
- 请求保持:线程持有已有锁,再申请新锁
- 不可剥夺:锁只能主动释放,不能被抢占
- 循环等待:线程间形成环路等待
4.3 避免死锁方案
- 统一锁获取顺序(所有线程先拿 lockA,再拿 lockB)
- 设置锁超时时间
- 减少嵌套同步代码块
五、线程池 7 大参数详解

频繁创建销毁线程开销极大,生产环境一律使用线程池ThreadPoolExecutor,核心 7 个构造参数:
java
运行
public ThreadPoolExecutor(
int corePoolSize, // 1.核心线程数
int maximumPoolSize, // 2.最大线程数
long keepAliveTime, // 3.非核心线程空闲存活时间
TimeUnit unit, // 4.时间单位
BlockingQueue<Runnable> workQueue, // 5.阻塞队列
ThreadFactory threadFactory, // 6.线程工厂(自定义线程名)
RejectedExecutionHandler handler // 7.拒绝策略
)
参数逐条解析
- corePoolSize:常驻核心线程,无任务也不会销毁
- maximumPoolSize:核心 + 临时线程总数,临时线程 = max-core
- keepAliveTime:临时线程空闲多久后回收
- unit:时间单位(秒、毫秒、分钟)
- workQueue:等待队列,常用
ArrayBlockingQueue、LinkedBlockingQueue - threadFactory:自定义线程命名,方便日志排查
- handler 拒绝策略(4 种内置)
- AbortPolicy:直接抛出异常(默认)
- CallerRunsPolicy:交给调用者线程执行
- DiscardPolicy:丢弃当前任务
- DiscardOldestPolicy:丢弃队列队首等待最久任务
线程池执行流程
- 任务进来,核心线程数未满 → 创建核心线程执行
- 核心线程已满 → 任务放入阻塞队列
- 队列满了 → 创建临时线程,直到达到 maxPoolSize
- 总线程达最大值 + 队列已满 → 触发拒绝策略
基础线程池代码示例
java
运行
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
// 提交10个任务测试
for (int i = 1; i <= 10; i++) {
int taskNum = i;
pool.submit(() -> {
System.out.println("执行任务:" + taskNum + " 线程:" + Thread.currentThread().getName());
});
}
pool.shutdown();
}
}
六、CompletableFuture 异步编程入门

JDK8 推出CompletableFuture,替代老旧Future,支持链式回调、多任务组合,简化异步开发。
6.1 无返回异步任务 runAsync
java
运行
// 线程池执行异步任务
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("异步任务执行:" + Thread.currentThread().getName());
}, pool);
future.get(); // 阻塞等待完成
6.2 有返回异步任务 supplyAsync
java
运行
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 100 * 2;
}, pool);
// 任务完成回调
future.thenAccept(res -> System.out.println("结果:" + res));
6.3 多任务组合常用 API
thenCombine:两个异步任务全部完成后合并结果anyOf:任意一个任务完成就执行回调allOf:等待所有任务全部执行完毕
java
运行
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
// 合并两个结果求和
CompletableFuture<Integer> combine = f1.thenCombine(f2, (a, b) -> a + b);
System.out.println(combine.get()); // 输出30
七、总结
- 线程创建三种方式:
Thread、Runnable、Callable+Future,带返回值优先 Callable synchronized解决共享资源线程安全,嵌套锁易引发死锁,遵循统一锁顺序规避- 生产禁止使用
Executors快捷创建线程池,手动ThreadPoolExecutor自定义 7 大参数,控制资源 - JDK8
CompletableFuture简化异步编排,替代传统 Future,适合接口并行查询、多任务处理
更多推荐
所有评论(0)