Java多线程wait/notify无法传递JSONObject问题咨询
问题核心原因
JSONObject 完全支持在线程间通过 wait/notify 机制传递,你遇到的问题和对象类型是 JSONObject 还是自定义业务类没有任何关系,本质是代码里的锁对象根本不是同一个实例。
你代码里的错误点非常明确:
- 第一个线程中你先初始化了一个空的
JSONObject实例赋值给jobj_gnss,但在JSON解析完成后,你将jobj_gnss变量重新指向了parser.parse()返回的全新JSONObject实例。后续你加锁、调用notifyAll()操作的都是这个新生成的解析结果对象,和最开始创建的空JSONObject没有任何关联。 - 第二个线程中的
jobj_gnss变量没有做任何初始化:如果是局部变量,这段代码直接编译不通过;如果是成员变量,默认值为null,直接对null做synchronized加锁会直接抛出空指针异常。就算你提前把第二个线程的jobj_gnss赋值为第一个线程最开始创建的空JSONObject实例,两个线程操作的也不是同一个锁:第一个线程notify的是解析生成的新对象,第二个线程wait的是最开始的空对象,永远收不到通知,自然拿不到解析后的数据。
你之前测试自定义业务类能正常运行,只是因为你没有在同步逻辑里重新给共享变量赋值换对象,两个线程始终在同一个实例上做wait/notify,和对象类型无关。
修正实现方案
不要用业务数据对象当锁,单独定义一个不可变的专门锁对象,同时用独立的共享变量存储解析结果,从根源上避免锁对象引用变更的问题:
首先定义两个线程共享的变量:
// 专用锁对象,初始化后永远不重新赋值 private final Object lock = new Object(); // 共享变量存储解析后的JSON结果,volatile保证跨线程可见性 private volatile JSONObject jsonResult = null;
数据获取线程(第一个线程)代码:
String testingProcess="{\"navi\":{\"longitude\":9.015089833333333,\"latitude\":45.514222999999997,\"altitude\":151.9,\"speed\":78.2,\"travelAngle\":244.08,\"horp\":2.52,\"vertp\":5.699999999999999},\"status\":{\"locked\":true,\"quality\":2,\"type\":3,\"gps_time\":\"10h 57m 20s October 13 2020\",\"satellites_tracked\":12,\"satellites_in_view\":15},\"res\":\"OK\"}"; JSONParser parser = new JSONParser(); try { jsonResult = (JSONObject) parser.parse(testingProcess); } catch (ParseException e) { e.printStackTrace(); } // 始终锁固定的lock对象 synchronized (lock) { lock.notifyAll(); }
数据处理线程(第二个线程)代码:
synchronized (lock) { // 必须用while循环判断条件,防止虚假唤醒 while (jsonResult == null) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } // 走到这里说明结果已经准备完成,直接处理即可 System.out.println(jsonResult.toString()); // 后续持久化到文件的逻辑 }
额外注意事项
- 永远不要把可能被重新赋值的变量指向的对象作为synchronized锁,否则极易出现锁不一致导致的通知丢失、死锁问题。
- 调用wait()等待条件时必须用while循环判断业务状态,不能用if判断,避免线程虚假唤醒后在条件未满足的情况下继续执行。
- 跨线程访问的共享变量建议加
volatile修饰,保证一个线程对变量的修改对其他线程立即可见,避免JMM可见性问题。
内容的提问来源于stack exchange,提问作者Nfernandez
相关产品推荐
相关产品推荐

