Java 多线程与并发编程实战——从基础到常用并发工具
·
多线程是 Java 后端开发的核心技能,也是面试中的高频考点。很多初学者觉得多线程难,其实掌握了核心概念和常用工具后,并不复杂。
一、创建线程的三种方式
// 1. 继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
}
new MyThread().start();
// 2. 实现 Runnable 接口(推荐)
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("任务执行中");
}
}
new Thread(new MyTask()).start();
// 3. 实现 Callable 接口(带返回值)
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "任务执行完毕";
}
}
FutureTask<String> ft = new FutureTask<>(new MyCallable());
new Thread(ft).start();
String result = ft.get(); // 获取返回值
推荐优先使用 Runnable 或 Callable,比继承 Thread 更灵活,也能继续继承其他类。
二、线程生命周期
新建(NEW)→ 就绪(RUNNABLE)→ 运行(RUNNING)→ 阻塞(BLOCKED/WAITING)→ 死亡(TERMINATED)
Thread t = new Thread(() -> {
System.out.println("运行中");
});
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
Thread.sleep(100);
System.out.println(t.getState()); // TERMINATED
三、线程安全问题
多个线程同时访问共享数据,就会产生线程安全问题:
// ❌ 线程不安全的例子
class Counter {
private int count = 0;
public void increment() { count++; } // 不是原子操作!
public int getCount() { return count; }
}
// 测试:1000个线程各加1,结果很可能小于1000
解决方案:
// ✅ 方式一:synchronized 同步锁
class SafeCounter {
private int count = 0;
public synchronized void increment() { count++; }
// 或者使用同步块
public void increment() {
synchronized (this) { count++; }
}
}
// ✅ 方式二:Lock 锁
class LockCounter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try { count++; }
finally { lock.unlock(); }
}
}
// ✅ 方式三:原子类(推荐,性能最好)
class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() { count.incrementAndGet(); }
}
四、线程池——生产中禁止手动 new Thread
// 创建线程池的两种方式
// 1. 通过 Executors 工具类(不推荐,有坑)
ExecutorService pool1 = Executors.newFixedThreadPool(5); // 队列无界,可能OOM
ExecutorService pool2 = Executors.newCachedThreadPool(); // 线程无界,可能OOM
// 2. 直接 new ThreadPoolExecutor(推荐)
ExecutorService pool = new ThreadPoolExecutor(
2, // corePoolSize:核心线程数
5, // maximumPoolSize:最大线程数
60L, // keepAliveTime:空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(10), // 阻塞队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// 使用
pool.execute(() -> System.out.println("执行任务"));
Future<String> result = pool.submit(() -> "任务结果");
// 关闭
pool.shutdown(); // 不再接收新任务,已提交的继续执行
线程池的7个核心参数:
| 参数 | 作用 |
|---|---|
| corePoolSize | 核心线程数(常驻) |
| maximumPoolSize | 最大线程数 |
| keepAliveTime | 非核心线程空闲最大存活时间 |
| unit | 时间单位 |
| workQueue | 任务队列(存等待的任务) |
| threadFactory | 线程工厂(可自定义线程名) |
| handler | 拒绝策略(队列+线程池都满了怎么办) |
拒绝策略:
AbortPolicy:抛异常(默认)CallerRunsPolicy:调用者线程自己执行DiscardPolicy:直接丢掉,不报错DiscardOldestPolicy:丢掉队首最旧任务
五、常用并发工具类
1. CountDownLatch——等所有线程完成
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 执行完毕");
latch.countDown(); // 计数器减1
}).start();
}
latch.await(); // 等待计数器归零
System.out.println("所有任务完成,继续执行");
2. CyclicBarrier——线程互相等待
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有人都到了,开始开会!");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 到达会议室");
barrier.await(); // 等待其他人
}).start();
}
3. Semaphore——限流
Semaphore semaphore = new Semaphore(3); // 最多3个线程同时访问
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 开始处理");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 处理完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可证
}
}).start();
}
六、volatile 关键字
// volatile 保证可见性,不保证原子性
public class Flag {
private volatile boolean running = true;
public void stop() { running = false; }
public void run() {
while (running) {
// 没有 volatile,另一个线程改了 running,这里可能看不到
}
}
}
volatile 适用场景: 一个线程写、多个线程读的标记位变量。
七、实际开发建议
// 企业级项目中的线程池配置示例
@Configuration
public class ThreadPoolConfig {
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("async-task-");
// 拒绝策略:由调用者线程执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
// 使用 @Async 异步执行
@Service
public class AsyncService {
@Async("taskExecutor")
public void sendNotification(Long userId) {
// 异步发送通知,不阻塞主流程
System.out.println("异步发送通知给用户: " + userId);
}
}
总结
多线程开发的核心要点就四个:
线程安全 → 加锁或原子类
线程管理 → 用线程池,别 new Thread()
线程协作 → CountDownLatch / CyclicBarrier / Semaphore
标记共享 → volatile
掌握了这些,日常开发中的并发场景基本都能应付。
💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。
更多推荐
所有评论(0)