多线程环境下JsonPath 2.9.0返回错误值,寻求解决办法及替代方案
问题根源
这确实是JsonPath 2.9.0本身的问题。JsonPath的JsonPath.read()静态方法内部会复用全局的配置/上下文实例,这些实例并非线程安全,多线程并发调用时会出现上下文污染,导致不同线程的数据互相串用,就像你测试里出现的Babara50、John48这类交叉结果。
应对方法
为每个线程创建独立的JsonPath上下文
不要使用静态的JsonPath.read(),手动为每个线程创建专属的Configuration和JsonPath实例,避免共享上下文:private Runnable createRunnable(String json) { return () -> { int count = 10; // 每个线程初始化独立的配置与JsonPath实例 Configuration config = Configuration.defaultConfiguration(); JsonPath jsonPath = JsonPath.compile("concat($.person.name, $.person.age)"); try { while (count > 0) { count--; Thread.sleep(1000); String value = jsonPath.read(json, config); if (value.equals("John48") || value.equals("Babara50")) { System.out.println("Wrong value returned: " + value); } System.out.printf("Thread-[%d], Value: [%s]%n", Thread.currentThread().getId(), value); } } catch (Exception e) { e.printStackTrace(); } }; }升级JsonPath版本
后续的JsonPath稳定版本(如高于2.9.0的版本)对线程安全问题做了修复优化,升级到最新稳定版可直接解决这类问题,注意升级后验证原有功能兼容性。用ThreadLocal管理线程隔离的上下文
若需要复用上下文同时保证线程隔离,可通过ThreadLocal存储每个线程的Configuration或JsonPath实例:private static final ThreadLocal<JsonPath> JSON_PATH_THREAD_LOCAL = ThreadLocal.withInitial( () -> JsonPath.compile("concat($.person.name, $.person.age)") ); private static final ThreadLocal<Configuration> CONFIG_THREAD_LOCAL = ThreadLocal.withInitial( Configuration::defaultConfiguration ); private Runnable createRunnable(String json) { return () -> { int count = 10; try { while (count > 0) { count--; Thread.sleep(1000); String value = JSON_PATH_THREAD_LOCAL.get().read(json, CONFIG_THREAD_LOCAL.get()); if (value.equals("John48") || value.equals("Babara50")) { System.out.println("Wrong value returned: " + value); } System.out.printf("Thread-[%d], Value: [%s]%n", Thread.currentThread().getId(), value); } } catch (Exception e) { e.printStackTrace(); } finally { // 可选:线程结束时清理ThreadLocal,避免内存泄漏 JSON_PATH_THREAD_LOCAL.remove(); CONFIG_THREAD_LOCAL.remove(); } }; }
原测试代码与运行结果
原测试代码
@Test public void testJsonPath() throws IOException { String jsonString = "{\"test\":1,\"person\":{\"name\":\"John\",\"age\":50}}"; String jsonString2 ="{\"test\":1,\"person\":{\"name\":\"Babara\",\"age\":48}}"; Thread thread1 = new Thread(createRunnable(jsonString)); Thread thread2 = new Thread(createRunnable(jsonString2)); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } private Runnable createRunnable(String json) { return () -> { int count = 10; try { while (count > 0) { count--; Thread.sleep(1000); String value = JsonPath.read(json, "concat($.person.name, $.person.age)"); if (value.equals("John48") || value.equals("Babara50")) { System.out.println("Wrong value returned: " + value); } System.out.printf("Thread-[%d], Value: [%s]%n", Thread.currentThread().getId(), value); } } catch (Exception e) { e.printStackTrace(); } }; }
原运行结果
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [Babara50]
Thread-[31], Value: [John48]
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [Babara50]
Thread-[30], Value: [John50]
Thread-[31], Value: [John48]
Thread-[31], Value: [John48]
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [John50]
Thread-[31], Value: [Babara48]
Thread-[30], Value: [John50]
内容的提问来源于stack exchange,提问作者PhuTa

