5.9 KiB
5.9 KiB
并发编程
目录
并发概念
并发 vs 并行
// 并发:同一时间段内多个任务交替执行
// 单核 CPU:时间片轮转
// 并行:同一时刻多个任务同时执行
// 多核 CPU:真正同时执行
并发问题
// 问题1:竞态条件
private int count = 0;
public void increment() {
count++; // 非原子操作
// 1. 读取 count
// 2. count + 1
// 3. 写入 count
// 多线程同时执行可能导致数据错误
}
// 问题2:可见性问题
private boolean flag = false;
// 线程1
flag = true;
// 线程2
while (!flag) {
// 可能永远循环(可见性问题)
}
同步与异步
同步
// 同步:按顺序执行,等待结果
public void syncMethod() {
String result = loadData(); // 等待加载完成
updateUI(result);
}
异步
// 异步:不等待结果,继续执行
public void asyncMethod() {
loadDataAsync(new Callback() {
@Override
public void onResult(String result) {
updateUI(result);
}
});
// 继续执行其他代码
}
锁机制
synchronized
// 同步方法
public synchronized void method() {
// 临界区代码
}
// 同步代码块
public void method() {
synchronized (this) {
// 临界区代码
}
}
// 同步静态方法
public static synchronized void staticMethod() {
// 临界区代码
}
ReentrantLock
// ReentrantLock:可重入锁
private Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
// 可中断锁
lock.lockInterruptibly();
try {
// 临界区代码
} finally {
lock.unlock();
}
ReadWriteLock
// ReadWriteLock:读写锁
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.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();
}
}
原子类
AtomicInteger
// AtomicInteger:原子整数
private AtomicInteger count = new AtomicInteger(0);
// 原子操作
count.incrementAndGet(); // 原子递增
count.getAndIncrement(); // 先获取再递增
count.addAndGet(10); // 原子加法
AtomicReference
// AtomicReference:原子引用
private AtomicReference<String> ref = new AtomicReference<>("initial");
// 原子更新
ref.compareAndSet("initial", "new"); // CAS 操作
CAS 操作
// CAS:Compare And Swap
// 比较并交换
// 如果当前值等于期望值,则更新为新值
// 实现
public boolean compareAndSet(int expect, int update) {
if (value == expect) {
value = update;
return true;
}
return false;
}
并发集合
ConcurrentHashMap
// ConcurrentHashMap:线程安全的 HashMap
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("key", "value");
String value = map.get("key");
// 特点:
// - 分段锁机制
// - 支持并发读写
// - 性能优于 Hashtable
CopyOnWriteArrayList
// CopyOnWriteArrayList:线程安全的 ArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item");
String item = list.get(0);
// 特点:
// - 写时复制
// - 读操作无锁
// - 适合读多写少场景
BlockingQueue
// BlockingQueue:阻塞队列
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 生产者
queue.put("item"); // 阻塞直到有空间
// 消费者
String item = queue.take(); // 阻塞直到有元素
并发编程最佳实践
1. 避免死锁
// ❌ 问题:可能死锁
synchronized (lock1) {
synchronized (lock2) {
// 代码
}
}
// 另一个线程
synchronized (lock2) {
synchronized (lock1) {
// 代码
}
}
// ✅ 解决:按相同顺序获取锁
synchronized (lock1) {
synchronized (lock2) {
// 代码
}
}
2. 使用并发集合
// ✅ 使用并发集合
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
// ❌ 使用同步包装
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
3. 最小化锁范围
// ✅ 最小化锁范围
public void method() {
// 不需要同步的代码
synchronized (this) {
// 需要同步的代码
}
// 不需要同步的代码
}
// ❌ 扩大锁范围
public synchronized void method() {
// 所有代码都在锁内
}
面试常见问题
Q1: 并发和并行的区别?
答案:
- 并发:同一时间段内多个任务交替执行
- 并行:同一时刻多个任务同时执行
Q2: 并发问题有哪些?
答案:
- 竞态条件:多线程同时修改共享数据
- 可见性问题:线程修改数据其他线程看不到
- 死锁:多个线程互相等待
Q3: synchronized 和 Lock 的区别?
答案:
- synchronized:JVM 层面,自动释放锁
- Lock:API 层面,手动释放锁,更灵活
Q4: CAS 操作?
答案:
- Compare And Swap:比较并交换
- 原子操作,无锁编程
- 如果当前值等于期望值,则更新
Q5: 并发集合有哪些?
答案:
- ConcurrentHashMap:线程安全的 HashMap
- CopyOnWriteArrayList:线程安全的 ArrayList
- BlockingQueue:阻塞队列
最后更新:2024年