IOUtils.closeQuietly()是否线程安全?如何解决其引发的NPE问题?
解决IOUtils.closeQuietly调用DeflaterInputStream时的NPE问题
问题根源
从栈追踪和DeflaterInputStream的close代码来看,多线程重复调用close引发了竞态条件:
- 线程A进入
if (in != null)分支后,在finally块中将in置为null; - 线程B此时已经通过了
if (in != null)的检查,后续执行in.close()或def.end()时,由于in已被线程A置为null,直接触发NullPointerException。
无需synchronized的解决办法
1. 业务层控制close只执行一次
用volatile标记位保证多线程可见性,确保close逻辑仅被执行一次:
private volatile boolean isClosed = false; private DeflaterInputStream deflaterStream; // 关闭方法 public void safeCloseStream() { if (!isClosed) { isClosed = true; IOUtils.closeQuietly(deflaterStream); } }
volatile关键字能让所有线程实时感知到isClosed的状态变化,避免多个线程同时进入关闭流程。
2. 用原子类实现原子性关闭检查
借助AtomicBoolean的原子操作,确保只有一个线程能执行关闭逻辑:
private AtomicBoolean closedFlag = new AtomicBoolean(false); private DeflaterInputStream deflaterStream; public void safeCloseStream() { // 仅当flag为false时才执行关闭,并原子性置为true if (closedFlag.compareAndSet(false, true)) { try { if (deflaterStream != null) { deflaterStream.close(); } } catch (IOException ignored) { // 静默处理异常,对齐IOUtils.closeQuietly的行为 } } }
这种方式比synchronized更轻量,原子操作的性能开销远低于锁竞争。
3. 替换为线程安全的流包装类
如果业务场景允许,可以用SynchronizedInputStream包装DeflaterInputStream,让所有流操作(包括close)都自带线程安全:
DeflaterInputStream deflaterStream = new DeflaterInputStream(originalStream); InputStream safeStream = new SynchronizedInputStream(deflaterStream); // 后续操作safeStream,关闭时直接调用IOUtils.closeQuietly(safeStream)
注意:这种方式会给所有流操作加锁,性能开销略高于前两种,但实现简单。
内容的提问来源于stack exchange,提问作者nc_411
相关产品推荐
相关产品推荐

