并发编程常用写法
并发编程是Java开发中的重要技能。本文详细介绍并发编程的常用写法和最佳实践,包含线程池、锁、并发集合等。
📋 目录
🎯 一、线程池使用
1.1 创建线程池
使用Executors(简单场景):
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 固定大小线程池
ExecutorService executor1 = Executors.newFixedThreadPool(10);
// 缓存线程池
ExecutorService executor2 = Executors.newCachedThreadPool();
// 单线程池
ExecutorService executor3 = Executors.newSingleThreadExecutor();
// 定时任务线程池
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5);使用ThreadPoolExecutor(推荐):
java
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 工作队列
new ThreadFactory() { // 线程工厂
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + System.currentTimeMillis());
t.setDaemon(false);
return t;
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);1.2 提交任务
提交任务:
java
ExecutorService executor = Executors.newFixedThreadPool(10);
// 提交Runnable任务
executor.submit(() -> {
System.out.println("执行任务");
});
// 提交Callable任务
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "任务完成";
});
// 获取结果
try {
String result = future.get(); // 阻塞等待结果
String result2 = future.get(5, TimeUnit.SECONDS); // 带超时
} catch (Exception e) {
// 处理异常
}
// 批量提交任务
List<Callable<String>> tasks = Arrays.asList(
() -> "任务1",
() -> "任务2",
() -> "任务3"
);
List<Future<String>> futures = executor.invokeAll(tasks);1.3 线程池关闭
关闭线程池:
java
// 优雅关闭
executor.shutdown(); // 不接受新任务,等待已有任务完成
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未正常关闭");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}🎯 二、锁机制
2.1 synchronized
synchronized使用:
java
// 同步方法
public synchronized void method() {
// 同步代码块
}
// 同步代码块
public void method() {
synchronized (this) {
// 同步代码
}
}
// 同步类方法
public static synchronized void staticMethod() {
// 同步静态方法
}
// 同步类对象
public void method() {
synchronized (MyClass.class) {
// 同步代码
}
}2.2 ReentrantLock
ReentrantLock使用:
java
import java.util.concurrent.locks.ReentrantLock;
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock(); // 确保锁被释放
}
}
// 可中断锁
public void interruptibleMethod() throws InterruptedException {
lock.lockInterruptibly();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
// 尝试获取锁
public boolean tryLockMethod() {
if (lock.tryLock()) {
try {
// 临界区代码
return true;
} finally {
lock.unlock();
}
}
return false;
}
// 带超时的尝试获取锁
public boolean tryLockWithTimeout() throws InterruptedException {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 临界区代码
return true;
} finally {
lock.unlock();
}
}
return false;
}2.3 ReadWriteLock
读写锁:
java
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
// 读操作
public String read() {
readLock.lock();
try {
// 读操作(可并发)
return data;
} finally {
readLock.unlock();
}
}
// 写操作
public void write(String value) {
writeLock.lock();
try {
// 写操作(互斥)
data = value;
} finally {
writeLock.unlock();
}
}🎯 三、并发集合
3.1 并发集合使用
ConcurrentHashMap:
java
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 基本操作
map.put("key", 1);
Integer value = map.get("key");
map.remove("key");
// 原子操作
map.putIfAbsent("key", 1); // 如果不存在才放入
map.replace("key", 1, 2); // 如果值为1才替换为2
map.compute("key", (k, v) -> v == null ? 1 : v + 1);
// 遍历(弱一致性)
map.forEach((key, value) -> System.out.println(key + "=" + value));CopyOnWriteArrayList:
java
import java.util.concurrent.CopyOnWriteArrayList;
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 基本操作
list.add("a");
list.get(0);
list.remove(0);
// 遍历(不会抛出ConcurrentModificationException)
for (String item : list) {
// 安全遍历
}BlockingQueue:
java
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100);
// 添加元素(阻塞)
queue.put("item"); // 队列满时阻塞
// 获取元素(阻塞)
String item = queue.take(); // 队列空时阻塞
// 非阻塞操作
boolean added = queue.offer("item"); // 返回是否成功
String item2 = queue.poll(); // 返回元素或null
// 带超时的操作
boolean added2 = queue.offer("item", 5, TimeUnit.SECONDS);
String item3 = queue.poll(5, TimeUnit.SECONDS);🎯 四、原子类
4.1 原子类使用
AtomicInteger:
java
import java.util.concurrent.atomic.AtomicInteger;
AtomicInteger counter = new AtomicInteger(0);
// 基本操作
counter.incrementAndGet(); // 先加1再返回
counter.getAndIncrement(); // 先返回再加1
counter.addAndGet(10); // 加10后返回
counter.getAndAdd(10); // 先返回再加10
// 比较并设置
boolean success = counter.compareAndSet(0, 10); // 如果当前值为0,则设置为10
// 获取并更新
int oldValue = counter.getAndUpdate(x -> x * 2);
// 更新并获取
int newValue = counter.updateAndGet(x -> x + 1);AtomicLong:
java
import java.util.concurrent.atomic.AtomicLong;
AtomicLong counter = new AtomicLong(0L);
// 与AtomicInteger类似的操作
counter.incrementAndGet();
counter.compareAndSet(0L, 100L);AtomicReference:
java
import java.util.concurrent.atomic.AtomicReference;
AtomicReference<String> reference = new AtomicReference<>("初始值");
// 获取和设置
String value = reference.get();
reference.set("新值");
// 比较并设置
boolean success = reference.compareAndSet("初始值", "新值");
// 获取并更新
String oldValue = reference.getAndUpdate(s -> s + "更新");📖 学习资源
官方文档
书籍推荐
- 《Java并发编程实战》 - Brian Goetz
✅ 学习检查清单
- [ ] 已掌握线程池的使用
- [ ] 已掌握锁机制
- [ ] 已熟悉并发集合
- [ ] 已了解原子类的使用