深入理解Java多线程与并发编程:从基础到实践
通过本文的介绍,希望读者能够掌握Java多线程的核心概念和最佳实践。在实际开发中,建议:优先考虑使用包中的高级工具谨慎使用低级别的同步机制充分测试多线程代码。
多线程与并发编程是Java开发中至关重要的高级特性,合理使用可以显著提升程序性能,但若使用不当则会导致各种难以调试的问题。本文将系统性地介绍Java多线程的核心概念和最佳实践,帮助开发者掌握这一强大工具。
一、多线程基础实现
1.1 继承Thread类
继承Thread类是最基础的多线程实现方式:
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); // 启动线程1
t2.start(); // 启动线程2
}
}
关键点:
-
必须重写
run()方法 -
通过
start()方法启动线程,而非直接调用run() -
每个线程都有独立的调用栈
1.2 实现Runnable接口
更推荐的实现方式,因为Java不支持多重继承:
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable执行: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
}
优势:
-
避免单继承限制
-
更适合资源共享
-
与线程池等高级特性配合更好
1.3 Java 8 Lambda简化写法
new Thread(() -> {
System.out.println("Lambda线程: " + Thread.currentThread().getName());
}).start();
二、线程同步与线程安全
2.1 synchronized关键字
方法同步:
public synchronized void increment() {
count++;
}
代码块同步:
public void increment() {
synchronized(this) {
count++;
}
}
静态方法同步:
public static synchronized void staticIncrement() {
staticCount++;
}
2.2 Lock接口及其实现
Java 5引入了更灵活的java.util.concurrent.locks.Lock接口:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
Lock vs synchronized:
-
Lock更灵活,可以尝试获取锁(tryLock)
-
Lock可以设置公平性
-
Lock提供了更丰富的功能(如Condition)
-
synchronized更简洁,自动释放锁
2.3 volatile关键字
private volatile boolean running = true;
public void stop() {
running = false;
}
适用场景:
-
保证变量的可见性
-
不保证原子性(不适合计数器场景)
-
比synchronized更轻量
三、线程池与Executor框架
3.1 为什么需要线程池
-
降低资源消耗(减少线程创建销毁开销)
-
提高响应速度(任务到达时线程已存在)
-
提高线程可管理性(统一分配、调优和监控)
3.2 Executor框架核心类
1. 创建固定大小线程池:
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
2. 创建可缓存线程池:
ExecutorService cachedPool = Executors.newCachedThreadPool();
3. 创建单线程池:
ExecutorService singleThread = Executors.newSingleThreadExecutor();
4. 创建定时任务线程池:
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
3.3 ThreadPoolExecutor详解
实际开发中建议直接使用ThreadPoolExecutor构造函数:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100) // 工作队列
);
关键参数解析:
-
核心线程数:池中保持的线程数量(即使空闲)
-
最大线程数:池中允许的最大线程数量
-
存活时间:超过核心线程数的空闲线程存活时间
-
工作队列:用于保存待执行任务的阻塞队列
-
拒绝策略:当任务无法执行时的处理策略
3.4 线程池最佳实践
-
合理配置线程数:
-
CPU密集型:CPU核心数 + 1
-
IO密集型:CPU核心数 * (1 + 平均等待时间/平均计算时间)
-
-
使用有界队列:避免无界队列导致OOM
-
明确拒绝策略:
-
AbortPolicy(默认):抛出RejectedExecutionException
-
CallerRunsPolicy:由调用线程执行该任务
-
DiscardPolicy:直接丢弃任务
-
DiscardOldestPolicy:丢弃队列最前面的任务
-
四、常见并发问题与解决方案
4.1 死锁与避免
死锁示例:
// 线程1
synchronized(resourceA) {
synchronized(resourceB) {
// 操作
}
}
// 线程2
synchronized(resourceB) {
synchronized(resourceA) {
// 操作
}
}
避免策略:
-
按固定顺序获取锁
-
使用tryLock设置超时
-
使用更高级别的并发工具
4.2 线程间通信
wait/notify机制:
public class SharedResource {
private boolean ready = false;
public synchronized void produce() {
ready = true;
notifyAll();
}
public synchronized void consume() throws InterruptedException {
while(!ready) {
wait();
}
// 消费逻辑
}
}
更现代的替代方案:
-
BlockingQueue -
CountDownLatch -
CyclicBarrier -
Semaphore
五、Java并发工具包(java.util.concurrent)
5.1 原子类(Atomic)
AtomicInteger counter = new AtomicInteger(0);
// 线程安全的自增
counter.incrementAndGet();
5.2 Concurrent集合
-
ConcurrentHashMap:线程安全的HashMap -
CopyOnWriteArrayList:读多写少的List -
BlockingQueue:阻塞队列实现
5.3 CountDownLatch
CountDownLatch latch = new CountDownLatch(3);
// 工作线程
new Thread(() -> {
// 工作
latch.countDown();
}).start();
// 主线程等待
latch.await();
六、最佳实践总结
-
优先使用高级并发工具:而不是直接使用wait/notify
-
尽量使用不可变对象:避免同步问题
-
缩小同步范围:只同步必要的代码块
-
文档化线程安全策略:明确类的线程安全级别
-
避免过度同步:可能导致性能问题或死锁
-
考虑使用并发集合:而非手动同步的集合
结语
多线程与并发编程是Java开发中的双刃剑,合理使用可以极大提升程序性能,使用不当则会导致各种难以调试的问题。通过本文的介绍,希望读者能够掌握Java多线程的核心概念和最佳实践。
在实际开发中,建议:
-
优先考虑使用
java.util.concurrent包中的高级工具 -
谨慎使用低级别的同步机制
-
充分测试多线程代码
-
保持代码简洁明了
如果你觉得本文有帮助,请点赞收藏!对于多线程与并发编程,你还有哪些疑问或经验分享?欢迎在评论区留言讨论!
更多推荐


所有评论(0)