多线程¶
📖 章节简介¶
本章将介绍Java的多线程编程,包括线程创建、线程同步和线程池的使用。
上图梳理了线程创建、同步控制与线程池三类并发能力。
🧵 线程基础¶
1. 创建线程¶
Java
// 创建线程的三种方式
public class ThreadCreation {
// 方式1:继承Thread类
static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("MyThread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 方式2:实现Runnable接口
static class MyRunnable implements Runnable { // implements 实现接口
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("MyRunnable: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// 使用方式1
MyThread thread1 = new MyThread();
thread1.start();
// 使用方式2
Thread thread2 = new Thread(new MyRunnable());
thread2.start();
// 使用方式3:Lambda表达式(Java 8+)
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda Thread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread3.start();
}
}
2. 线程状态¶
Java
// 线程状态
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
System.out.println("线程运行中...");
Thread.sleep(2000);
System.out.println("线程运行结束");
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
});
System.out.println("线程状态: " + thread.getState()); // NEW
thread.start();
System.out.println("线程状态: " + thread.getState()); // RUNNABLE
Thread.sleep(1000);
System.out.println("线程状态: " + thread.getState()); // TIMED_WAITING
thread.join();
System.out.println("线程状态: " + thread.getState()); // TERMINATED
}
}
🔒 线程同步¶
1. synchronized关键字¶
Java
// synchronized使用
public class SynchronizedExample {
// ⚠️ counter是static变量(类级别共享),所以必须用类锁(static synchronized
// 或 synchronized(SynchronizedExample.class))来保护。
// 如果用this锁(实例锁),不同实例之间无法互斥,仍然存在竞态条件。
private static int counter = 0;
// 同步方法(类锁 — 保护static变量的正确写法)
public static synchronized void increment() { // synchronized 同步方法,保证线程安全
counter++;
}
// 同步代码块(类锁)
public void decrement() {
synchronized (SynchronizedExample.class) {
counter--;
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedExample example = new SynchronizedExample();
// 创建多个线程
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
example.increment();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
thread.join();
}
System.out.println("最终计数: " + counter);
}
}
2. Lock接口¶
Java
// Lock使用
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final Lock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
LockExample example = new LockExample();
// 创建多个线程
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
example.increment();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
thread.join();
}
System.out.println("最终计数: " + example.counter);
}
}
🏊 线程池¶
1. ExecutorService¶
Java
// 线程池使用
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小的线程池
// ⚠️ 注意:Executors.newFixedThreadPool() 内部使用无界队列 (LinkedBlockingQueue),
// 在高负载下任务会无限堆积,可能导致 OOM。生产环境建议直接使用 ThreadPoolExecutor
// 并指定有界队列:new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy())
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务" + taskId + "开始执行,线程: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断标志
e.printStackTrace();
}
System.out.println("任务" + taskId + "执行完成");
});
}
// 关闭线程池
executor.shutdown();
try {
// 等待所有任务完成
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("任务未在指定时间内完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有任务完成");
}
}
2. Callable和Future¶
Java
// Callable和Future使用
import java.util.concurrent.*;
public class CallableFutureExample {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交Callable任务
Future<Integer> future1 = executor.submit(() -> {
Thread.sleep(2000);
return 100;
});
Future<Integer> future2 = executor.submit(() -> {
Thread.sleep(1000);
return 200;
});
Future<Integer> future3 = executor.submit(() -> {
Thread.sleep(3000);
return 300;
});
// 获取结果
System.out.println("任务1结果: " + future1.get());
System.out.println("任务2结果: " + future2.get());
System.out.println("任务3结果: " + future3.get());
executor.shutdown();
}
}
💡 最佳实践¶
1. 线程安全¶
Java
// 线程安全最佳实践
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadSafety {
// ✅ 好的做法:使用线程安全的类
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet();
}
// ❌ 不好的做法:使用非线程安全的操作
private int unsafeCounter = 0;
public void unsafeIncrement() {
unsafeCounter++; // 非线程安全
}
// ✅ 好的做法:使用并发集合
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// ❌ 不好的做法:使用非线程安全的集合
private HashMap<String, Integer> unsafeMap = new HashMap<>();
}
2. 性能优化¶
Java
// 多线程性能优化
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MultithreadingOptimization {
// ✅ 好的做法:使用线程池
private ExecutorService executor = Executors.newFixedThreadPool(10);
public void processTasks(List<Runnable> tasks) {
for (Runnable task : tasks) {
executor.submit(task);
}
}
// ❌ 不好的做法:为每个任务创建新线程
public void processTasksUnsafe(List<Runnable> tasks) {
for (Runnable task : tasks) {
new Thread(task).start(); // 资源浪费
}
}
// ✅ 好的做法:使用适当的锁粒度
private final Lock fineLock = new ReentrantLock();
public void fineGrainedLock() {
fineLock.lock();
try {
// 只锁定必要的代码
} finally {
fineLock.unlock();
}
}
}
📝 练习题¶
基础题¶
- 如何创建线程?
- synchronized和Lock有什么区别?
- 什么是线程池?
进阶题¶
- 实现生产者消费者模式。
- 使用线程池提高性能。
- 实现线程安全的数据结构。
实践题¶
- 创建一个多线程下载器。
- 实现一个简单的任务调度器。
- 构建一个并发计算器。
📚 推荐阅读¶
🔗 下一章¶
网络编程 - 学习Java的网络编程。