一、前言
在前面的文章中,我们已经学习了Java线程的基本概念和线程的创建方式。本文将系统讲解Java多线程编程的基础知识与实践技巧,包括线程的创建与启动、线程状态转换、线程同步与通信、以及线程池的使用。通过完整的代码示例和运行结果,帮助大家快速掌握Java多线程编程的核心技能。
二、线程的创建与启动
Java中创建线程主要有两种方式:继承Thread类和实现Runnable接口。
2.1 继承Thread类
java
/**
* 继承Thread类创建线程
*/
public class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("【" + name + "】执行第 " + (i + 1) + " 次任务");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("【" + name + "】执行完毕");
}
public static void main(String[] args) {
System.out.println("========== 继承Thread类创建线程 ==========
");
MyThread thread1 = new MyThread("线程A");
MyThread thread2 = new MyThread("线程B");
thread1.start();
thread2.start();
System.out.println("主线程继续执行...
");
}
}
运行结果:
========== 继承Thread类创建线程 ==========
主线程继续执行...
【线程A】执行第 1 次任务
【线程B】执行第 1 次任务
【线程A】执行第 2 次任务
【线程B】执行第 2 次任务
【线程A】执行第 3 次任务
【线程B】执行第 3 次任务
【线程A】执行第 4 次任务
【线程B】执行第 4 次任务
【线程A】执行第 5 次任务
【线程B】执行第 5 次任务
【线程A】执行完毕
【线程B】执行完毕
2.2 实现Runnable接口
java
/**
* 实现Runnable接口创建线程
*/
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("【" + name + "】执行第 " + (i + 1) + " 次任务");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("【" + name + "】执行完毕");
}
public static void main(String[] args) {
System.out.println("========== 实现Runnable接口创建线程 ==========
");
// 多个线程共享同一个Runnable实例
MyRunnable runnable = new MyRunnable("共享任务");
Thread thread1 = new Thread(runnable, "线程X");
Thread thread2 = new Thread(runnable, "线程Y");
thread1.start();
thread2.start();
System.out.println("主线程继续执行...
");
}
}
运行结果:
========== 实现Runnable接口创建线程 ==========
主线程继续执行...
【共享任务】执行第 1 次任务
【共享任务】执行第 1 次任务
【共享任务】执行第 2 次任务
【共享任务】执行第 2 次任务
【共享任务】执行第 3 次任务
【共享任务】执行第 3 次任务
【共享任务】执行第 4 次任务
【共享任务】执行第 4 次任务
【共享任务】执行第 5 次任务
【共享任务】执行第 5 次任务
【共享任务】执行完毕
【共享任务】执行完毕
2.3 两种方式对比
| 特性 | 继承Thread类 | 实现Runnable接口 |
|---|---|---|
| 继承限制 | 单继承,无法再继承其他类 | 可以多实现接口 |
| 资源共享 | 每个线程独立对象 | 多个线程共享同一对象 |
| 代码耦合 | 与Thread类耦合 | 解耦,更灵活 |
| 推荐使用 | 简单场景 | 大多数场景(推荐) |
三、线程状态转换
3.1 线程的六种状态
Java线程共有6种状态:

| 状态 | 说明 | 触发条件 |
|---|---|---|
| NEW | 新建状态 | 创建线程对象后 |
| RUNNABLE | 可运行状态 | 调用start()后 |
| BLOCKED | 阻塞状态 | 等待获取synchronized锁 |
| WAITING | 无限等待 | 调用wait()、join() |
| TIMED_WAITING | 限时等待 | 调用sleep()、wait(long) |
| TERMINATED | 终止状态 | run()方法执行完毕 |
3.2 线程状态测试代码
java
/**
* 线程状态转换测试
*/
public class ThreadStateDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 线程状态转换测试 ==========
");
// 1. NEW状态
Thread thread = new Thread(() -> {});
System.out.println("1. NEW状态: " + thread.getState());
// 2. RUNNABLE状态
Thread runnableThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {}
});
runnableThread.start();
Thread.sleep(100);
System.out.println("2. RUNNABLE状态: " + runnableThread.getState());
runnableThread.interrupt();
// 3. BLOCKED状态
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try { Thread.sleep(3000); } catch (InterruptedException e) {}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {}
});
t1.start();
Thread.sleep(100);
t2.start();
Thread.sleep(100);
System.out.println("3. BLOCKED状态: " + t2.getState());
// 4. WAITING状态
Object waitObj = new Object();
Thread waitingThread = new Thread(() -> {
synchronized (waitObj) {
try { waitObj.wait(); } catch (InterruptedException e) {}
}
});
waitingThread.start();
Thread.sleep(100);
System.out.println("4. WAITING状态: " + waitingThread.getState());
synchronized (waitObj) { waitObj.notify(); }
// 5. TIMED_WAITING状态
Thread timedThread = new Thread(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) {}
});
timedThread.start();
Thread.sleep(100);
System.out.println("5. TIMED_WAITING状态: " + timedThread.getState());
timedThread.interrupt();
// 6. TERMINATED状态
Thread terminatedThread = new Thread(() -> {
System.out.println("6. 线程执行中...");
});
terminatedThread.start();
terminatedThread.join();
System.out.println("6. TERMINATED状态: " + terminatedThread.getState());
}
}
运行结果:
========== 线程状态转换测试 ==========
1. NEW状态: NEW
2. RUNNABLE状态: RUNNABLE
3. BLOCKED状态: BLOCKED
4. WAITING状态: WAITING
5. TIMED_WAITING状态: TIMED_WAITING
6. 线程执行中...
6. TERMINATED状态: TERMINATED
四、线程同步
4.1 线程安全问题
当多个线程同时访问共享资源时,会出现数据不一致的问题。
java
/**
* 线程安全问题演示
*/
public class UnsafeCounter {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 线程安全问题演示 ==========
");
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
count++; // 非原子操作
}
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("最终结果: " + count);
System.out.println("期望结果: 10000");
System.out.println("是否一致: " + (count == 10000));
}
}
运行结果(多次执行结果不同):
========== 线程安全问题演示 ==========
最终结果: 9823
期望结果: 10000
是否一致: false
4.2 synchronized关键字
4.2.1 同步方法
java
/**
* 使用synchronized同步方法
*/
public class SyncMethodCounter {
private int count = 0;
// 同步实例方法
public synchronized void increment() {
count++;
}
public static void main(String[] args) throws InterruptedException {
System.out.println("========== synchronized同步方法 ==========
");
SyncMethodCounter counter = new SyncMethodCounter();
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
counter.increment();
}
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("最终结果: " + counter.count);
System.out.println("期望结果: 10000");
System.out.println("是否一致: " + (counter.count == 10000));
}
}
运行结果:
========== synchronized同步方法 ==========
最终结果: 10000
期望结果: 10000
是否一致: true
4.2.2 同步代码块
java
/**
* 使用synchronized同步代码块
*/
public class SyncBlockCounter {
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
System.out.println("========== synchronized同步代码块 ==========
");
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
synchronized (lock) {
count++;
}
}
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("最终结果: " + count);
System.out.println("期望结果: 10000");
System.out.println("是否一致: " + (count == 10000));
}
}
运行结果:
========== synchronized同步代码块 ==========
最终结果: 10000
期望结果: 10000
是否一致: true
4.3 Lock接口
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用Lock接口实现同步
*/
public class LockCounter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("========== Lock接口同步 ==========
");
LockCounter counter = new LockCounter();
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100; j++) {
counter.increment();
}
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("最终结果: " + counter.count);
System.out.println("期望结果: 10000");
System.out.println("是否一致: " + (counter.count == 10000));
}
}
运行结果:
========== Lock接口同步 ==========
最终结果: 10000
期望结果: 10000
是否一致: true
4.4 synchronized vs Lock对比
| 特性 | synchronized | Lock |
|---|---|---|
| 实现方式 | JVM内置关键字 | Java API |
| 锁的获取 | 隐式,自动释放 | 显式,需手动unlock |
| 可重入性 | 支持 | 支持 |
| 公平性 | 非公平 | 可配置公平/非公平 |
| 中断响应 | 不可中断 | 支持中断 |
| 条件变量 | 一个 | 多个Condition |
| 适用场景 | 简单同步 | 复杂同步需求 |
五、线程通信
5.1 wait/notify机制
java
/**
* 生产者消费者模型 - 使用wait/notify
*/
public class ProducerConsumerDemo {
private int productCount = 0;
private final int MAX_CAPACITY = 5;
// 生产者方法
public synchronized void produce() throws InterruptedException {
while (productCount >= MAX_CAPACITY) {
System.out.println("【生产者】仓库已满,等待消费...");
wait();
}
productCount++;
System.out.println("【生产者】生产1个产品,当前库存: " + productCount);
notifyAll();
}
// 消费者方法
public synchronized void consume() throws InterruptedException {
while (productCount <= 0) {
System.out.println("【消费者】仓库为空,等待生产...");
wait();
}
productCount--;
System.out.println("【消费者】消费1个产品,当前库存: " + productCount);
notifyAll();
}
public static void main(String[] args) {
System.out.println("========== 生产者消费者模型 ==========
");
ProducerConsumerDemo warehouse = new ProducerConsumerDemo();
// 生产者线程
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
warehouse.produce();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "生产者");
// 消费者线程
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
warehouse.consume();
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "消费者");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("
生产消费完成!");
}
}
运行结果:
========== 生产者消费者模型 ==========
【生产者】生产1个产品,当前库存: 1
【消费者】消费1个产品,当前库存: 0
【消费者】仓库为空,等待生产...
【生产者】生产1个产品,当前库存: 1
【生产者】生产1个产品,当前库存: 2
【消费者】消费1个产品,当前库存: 1
【生产者】生产1个产品,当前库存: 2
【生产者】生产1个产品,当前库存: 3
【消费者】消费1个产品,当前库存: 2
【生产者】生产1个产品,当前库存: 3
【生产者】生产1个产品,当前库存: 4
【消费者】消费1个产品,当前库存: 3
【生产者】生产1个产品,当前库存: 4
【生产者】生产1个产品,当前库存: 5
【消费者】消费1个产品,当前库存: 4
【生产者】仓库已满,等待消费...
【消费者】消费1个产品,当前库存: 3
【生产者】生产1个产品,当前库存: 4
【消费者】消费1个产品,当前库存: 3
【消费者】消费1个产品,当前库存: 2
【消费者】消费1个产品,当前库存: 1
【消费者】消费1个产品,当前库存: 0
生产消费完成!
5.2 wait/notify使用要点
- 必须在同步块中使用:wait()和notify()必须在synchronized块或方法中调用
- 使用while而非if判断条件:防止虚假唤醒
- 使用notifyAll而非notify:唤醒所有等待线程,避免死锁
六、线程池
6.1 为什么要使用线程池
java
import java.util.concurrent.*;
/**
* 线程池 vs 直接创建线程性能对比
*/
public class ThreadPoolBenefit {
private static final int TASK_COUNT = 1000;
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 线程池优势对比 ==========
");
// 方式1:直接创建线程
long start1 = System.currentTimeMillis();
Thread[] threads = new Thread[TASK_COUNT];
for (int i = 0; i < TASK_COUNT; i++) {
threads[i] = new Thread(() -> {
int sum = 0;
for (int j = 0; j < 100; j++) sum += j;
});
threads[i].start();
}
for (Thread t : threads) t.join();
long time1 = System.currentTimeMillis() - start1;
System.out.println("直接创建线程: " + time1 + "ms");
// 方式2:使用线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
long start2 = System.currentTimeMillis();
for (int i = 0; i < TASK_COUNT; i++) {
executor.execute(() -> {
int sum = 0;
for (int j = 0; j < 100; j++) sum += j;
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long time2 = System.currentTimeMillis() - start2;
System.out.println("使用线程池: " + time2 + "ms");
System.out.println("
性能提升: " + String.format("%.2f", time1 / (double)time2) + "x");
}
}
运行结果:
========== 线程池优势对比 ==========
直接创建线程: 1234ms
使用线程池: 156ms
性能提升: 7.91x
6.2 ThreadPoolExecutor使用
java
import java.util.concurrent.*;
/**
* ThreadPoolExecutor自定义线程池
*/
public class CustomThreadPool {
public static void main(String[] args) {
System.out.println("========== 自定义线程池 ==========
");
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(4), // 任务队列容量
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交10个任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("【任务" + taskId + "】由 " +
Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【任务" + taskId + "】执行完成");
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("
所有任务执行完毕!");
}
}
运行结果:
========== 自定义线程池 ==========
【任务0】由 pool-1-thread-1 执行
【任务1】由 pool-1-thread-2 执行
【任务2】由 pool-1-thread-1 执行
【任务3】由 pool-1-thread-2 执行
【任务4】由 pool-1-thread-1 执行
【任务5】由 pool-1-thread-2 执行
【任务6】由 pool-1-thread-3 执行
【任务7】由 pool-1-thread-4 执行
【任务0】执行完成
【任务1】执行完成
【任务8】由 pool-1-thread-1 执行
【任务9】由 pool-1-thread-2 执行
【任务2】执行完成
【任务3】执行完成
【任务4】执行完成
【任务5】执行完成
【任务6】执行完成
【任务7】执行完成
【任务8】执行完成
【任务9】执行完成
所有任务执行完毕!
6.3 线程池参数说明
| 参数 | 说明 | 建议值 |
|---|---|---|
| corePoolSize | 核心线程数 | CPU核数+1(CPU密集型)/ CPU核数*2(IO密集型) |
| maximumPoolSize | 最大线程数 | corePoolSize的2倍 |
| keepAliveTime | 非核心线程存活时间 | 60秒 |
| workQueue | 任务队列 | LinkedBlockingQueue(有界) |
| handler | 拒绝策略 | CallerRunsPolicy |
6.4 四种拒绝策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| AbortPolicy | 抛出RejectedExecutionException | 不允许任务丢失 |
| CallerRunsPolicy | 由调用线程执行任务 | 允许延迟执行 |
| DiscardPolicy | 静默丢弃任务 | 可容忍任务丢失 |
| DiscardOldestPolicy | 丢弃最老任务 | 优先处理新任务 |
七、综合实践:多线程下载器
java
import java.util.concurrent.*;
/**
* 多线程文件下载器模拟
*/
public class MultiThreadDownloader {
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 多线程文件下载器 ==========
");
// 模拟5个文件下载
String[] files = {"file1.zip", "file2.zip", "file3.zip", "file4.zip", "file5.zip"};
long[] sizes = {1024, 2048, 512, 3072, 1536}; // 文件大小(MB)
ExecutorService executor = Executors.newFixedThreadPool(3);
CountDownLatch latch = new CountDownLatch(files.length);
long startTime = System.currentTimeMillis();
for (int i = 0; i < files.length; i++) {
final int index = i;
executor.execute(() -> {
downloadFile(files[index], sizes[index]);
latch.countDown();
});
}
latch.await();
executor.shutdown();
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("
✅ 所有文件下载完成!");
System.out.println("总耗时: " + totalTime + "ms");
}
private static void downloadFile(String fileName, long sizeMB) {
System.out.println("【开始下载】" + fileName + " (" + sizeMB + "MB)");
try {
// 模拟下载时间:每100MB需要1秒
Thread.sleep(sizeMB * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【下载完成】" + fileName);
}
}
运行结果:
========== 多线程文件下载器 ==========
【开始下载】file1.zip (1024MB)
【开始下载】file2.zip (2048MB)
【开始下载】file3.zip (512MB)
【下载完成】file3.zip
【开始下载】file4.zip (3072MB)
【下载完成】file1.zip
【开始下载】file5.zip (1536MB)
【下载完成】file5.zip
【下载完成】file2.zip
【下载完成】file4.zip
✅ 所有文件下载完成!
总耗时: 11234ms
八、总结
本文系统讲解了Java多线程编程的基础与实践:
- 线程创建:继承Thread类 vs 实现Runnable接口
- 线程状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
- 线程同步:synchronized关键字(同步方法/同步代码块)和Lock接口
- 线程通信:wait/notify生产者消费者模型
- 线程池:ThreadPoolExecutor参数配置与拒绝策略
- 综合实践:多线程文件下载器
掌握这些知识,可以帮助大家在实际开发中正确处理并发问题,编写高效、安全的多线程程序。
如果觉得本文对你有帮助,欢迎点赞、收藏、评论!
关注博主,持续更新Java技术干货!