Java多线程读写JSON文件如何互斥?解决文件异常与损坏问题
多线程安全读写JSON文件的解决方案
1. 修复Semaphore的正确使用逻辑
你之前用Semaphore没解决问题,大概率是没把读-修改-写的完整流程包裹在信号量的临界区内,或者异常时没释放许可。正确的写法应该是:
@Override public void run() { File jsonFile = new File("你的JSON文件路径"); try { smp.acquire(); // 获取独占许可,进入临界区 // 读取JSON内容 String content = Files.readString(jsonFile.toPath()); JSONObject json = new JSONObject(content); // 执行你的更新操作 json.put("targetKey", "updatedValue"); // 写回文件 Files.writeString(jsonFile.toPath(), json.toString()); } catch (Exception e) { e.printStackTrace(); } finally { smp.release(); // 无论是否异常,必须释放许可 } }
核心是把文件操作的完整逻辑放在acquire和release之间,不能拆分锁读或锁写。
2. 用ReentrantLock替代Semaphore(更直观的独占锁)
ReentrantLock语义更清晰,专门用于独占式锁场景,同样要在finally块释放锁:
// 全局锁实例,所有MyTask共享同一个 Lock fileLock = new ReentrantLock(); // MyTask的run方法内 @Override public void run() { File jsonFile = new File("你的JSON文件路径"); fileLock.lock(); try { // 读JSON、修改、写回的完整逻辑 String content = Files.readString(jsonFile.toPath()); JSONObject json = new JSONObject(content); json.put("targetKey", "updatedValue"); Files.writeString(jsonFile.toPath(), json.toString()); } catch (Exception e) { e.printStackTrace(); } finally { fileLock.unlock(); } }
3. 文件系统级别的FileLock(跨进程安全)
如果需要跨JVM进程的线程安全,或者担心JVM层面锁失效,可以用NIO的FileLock:
@Override public void run() { File jsonFile = new File("你的JSON文件路径"); try (RandomAccessFile raf = new RandomAccessFile(jsonFile, "rw"); FileChannel channel = raf.getChannel()) { // 获取独占锁,通道关闭时自动释放 FileLock lock = channel.lock(); // 读取文件内容 raf.seek(0); byte[] buf = new byte[(int) channel.size()]; raf.read(buf); JSONObject json = new JSONObject(new String(buf)); // 修改内容 json.put("targetKey", "updatedValue"); // 清空原文件并写入新内容 raf.setLength(0); raf.seek(0); raf.write(json.toString().getBytes()); lock.release(); } catch (Exception e) { e.printStackTrace(); } }
4. 内存缓存+单线程写回(高并发场景优化)
如果并发量高,频繁锁文件会影响性能,可以把JSON内容缓存到内存,用线程安全容器存储,再由单线程统一处理持久化:
// 全局线程安全缓存,初始化时加载文件内容 private static final ConcurrentHashMap<String, JSONObject> jsonCache = new ConcurrentHashMap<>(); static { try { String content = Files.readString(Paths.get("你的JSON文件路径")); jsonCache.put("config", new JSONObject(content)); } catch (IOException e) { e.printStackTrace(); } } // MyTask只操作内存缓存 @Override public void run() { // 原子性修改缓存内容 jsonCache.compute("config", (key, oldJson) -> { oldJson.put("targetKey", "updatedValue"); return oldJson; }); // 用单线程池触发写回操作,避免多线程写文件 Executors.newSingleThreadExecutor().submit(() -> { try { Files.writeString(Paths.get("你的JSON文件路径"), jsonCache.get("config").toString()); } catch (IOException e) { e.printStackTrace(); } }); }
内容的提问来源于stack exchange,提问作者Eswari Sekar
相关产品推荐
相关产品推荐

