题目
设计支持高并发的线程安全下载管理器
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
线程同步机制,死锁预防,资源竞争避免,文件I/O优化,异常处理
快速回答
实现线程安全下载管理器的核心要点:
- 使用读写锁(ReadWriteLock)管理下载状态元数据
- 采用分段锁策略避免文件写入冲突
- 通过原子操作维护下载进度统计
- 实现超时和重试机制处理网络异常
- 使用内存映射文件(MappedByteBuffer)提升大文件写入性能
1. 核心设计原理
系统需要管理三个关键资源:
- 状态元数据:当前下载进度、分块状态等(高频读取)
- 网络连接:HTTP Range请求(并发操作)
- 文件存储:多线程写入同一文件(需要协调)
2. 线程同步实现
状态管理(读写锁示例):
class DownloadState {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<Integer, Long> chunkProgress = new ConcurrentHashMap<>();
public void updateProgress(int chunkId, long bytes) {
lock.writeLock().lock();
try {
chunkProgress.put(chunkId, chunkProgress.getOrDefault(chunkId, 0L) + bytes);
} finally {
lock.writeLock().unlock();
}
}
public long getTotalProgress() {
lock.readLock().lock();
try {
return chunkProgress.values().stream().mapToLong(Long::longValue).sum();
} finally {
lock.readLock().unlock();
}
}
}文件写入(分段锁策略):
class FileWriter {
private final RandomAccessFile file;
private final Lock[] segmentLocks; // 按文件段分片
public void write(long position, byte[] data) throws IOException {
int segment = (int)(position / SEGMENT_SIZE);
segmentLocks[segment].lock();
try {
file.seek(position);
file.write(data);
} finally {
segmentLocks[segment].unlock();
}
}
}3. 死锁预防策略
- 锁顺序协议:统一按 chunkId 顺序获取分段锁
- 超时机制:使用 tryLock(100ms, TimeUnit.MILLISECONDS)
- 资源分级:网络连接→状态锁→文件锁的固定获取顺序
4. 最佳实践
- 性能优化:
- 使用 MappedByteBuffer 实现零拷贝文件写入
- 线程池大小根据网络延迟动态调整(IO密集型)
- 容错机制:
- 校验写入数据的CRC32
- 失败分块自动重试(指数退避策略)
- 资源控制:
- 限制单个文件的并发线程数(避免磁盘抖动)
- 使用 Semaphore 控制总下载连接数
5. 常见错误
- 进度不一致:未使用原子操作更新状态导致统计偏差
- 文件损坏:多线程覆盖写入未同步
- 连接泄漏:网络异常未关闭HTTP连接
- 活锁风险:重试机制缺少随机延迟
6. 扩展知识
- 大文件处理:使用 FileChannel 的 transferFrom() 实现DMA传输
- 内存管理:DirectByteBuffer 避免JVM堆外内存溢出
- 分布式扩展:将分块状态存储在Redis等外部系统
- 流量控制:基于令牌桶算法限制下载速度