Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[BUG] JSONFactory和ObjectReaderProvider静态代码循环依赖导致死锁 #2994

Closed
yhzdys opened this issue Sep 29, 2024 · 10 comments
Closed
Labels
bug Something isn't working fixed
Milestone

Comments

@yhzdys
Copy link

yhzdys commented Sep 29, 2024

问题描述

项目中的一些组件需要一个独立的JSONUtil工具类,通过自定义ObjectReaderProvider来实现一些自定义的逻辑。
在项目启动时,如果有多个并发请求同时调用JSON的静态方法和自定义的JSONUtil,有概率出现线程死锁。

排查下来的原因是:

类ObjectReaderProvider和JSONFactory的静态代码块出现了循环依赖。
类加载器在初始化class时会给上一个class级别的锁以保证类的static代码只执行一次。

ObjectReaderProvider中的static代码块需要依赖JSONFactory完成初始化,
但是JSONFactory中的defaultObjectReaderProvider静态属性需要依赖ObjectReaderProvider完成初始化。

环境信息

  • OS信息: macOS 15.0
  • JDK信息:Oracle-jdk 17.0.2
  • 版本信息:Fastjson2 2.0.53

重现步骤

运行下面代码,可能出现死锁,导致系统无响应。

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);
        Thread thread1 = new Thread(() -> {
            System.out.println("1 start");
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10L)); // 停顿时间可能需要根据环境调整
            JSONUtil.test();
            latch.countDown();
        }, "thread-1");
        Thread thread2 = new Thread(() -> {
            System.out.println("2 start");
            JSON.parseObject("{}");
            latch.countDown();
        }, "thread-2");

        thread1.start();
        thread2.start();
        System.out.println("waiting");
        latch.await();
        System.out.println("finished");
    }

    public static class JSONUtil {

        private static final ObjectReaderProvider READER;
        private static final ObjectWriterProvider WRITER;

        static {
            // 解决方法,随便调一个JSONFactory的方法来保证加载顺序与默认顺序一致
//            JSONFactory.getDefaultObjectReaderProvider();

            READER = new ObjectReaderProvider();
            WRITER = new ObjectWriterProvider();
        }

        public static String test() {
            return "ok";
        }
    }
}

期待的正确结果

目前时通过JSONUtil中注释掉的方法来保证类的加载顺序一致。
但是这种静态代码循环依赖的形式,并非一种好的实现,一旦出现问题很难排查,或许有改进的必要?

相关日志输出

1 start
2 start
waiting

附加信息

@yhzdys yhzdys added the bug Something isn't working label Sep 29, 2024
@mek1986
Copy link
Contributor

mek1986 commented Oct 8, 2024

你提供的测试运用,我运行了很多次,都没有卡住。
请问你自己运行,大概多大概率会卡死呢?

@yhzdys
Copy link
Author

yhzdys commented Oct 8, 2024

你提供的测试运用,我运行了很多次,都没有卡住。 请问你自己运行,大概多大概率会卡死呢?

我本地是100%复现的

不同配置的机器,可能要改一下下面代码中的停顿时间,模拟一下业务逻辑的耗时

LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10L)); // 停顿时间可能需要根据环境调整

@yhzdys
Copy link
Author

yhzdys commented Oct 8, 2024

可以参考我本地运行时dump的信息

image

image

@mek1986
Copy link
Contributor

mek1986 commented Oct 8, 2024

可以参考我本地运行时dump的信息

image

image

嗯,看到了。

@mek1986
Copy link
Contributor

mek1986 commented Oct 9, 2024

@yhzdys 你有什么好的想法吗?

@yhzdys
Copy link
Author

yhzdys commented Oct 9, 2024

@yhzdys 你有什么好的想法吗?

解决循环依赖,一般是加个中间类来解决(没什么是加中间层解决不了的😂)

ObjectReaderProvider用到了JSONFactory的方法有6个

  • getProperty
  • isDisableReferenceDetect
  • isDisableArrayMapping
  • isDisableJSONB
  • isDisableAutoType
  • isDisableSmartMatch

我的想法是可以独立出一个JSONConfig类,把JSONFactory里的DEFAULT_PROPERTIES和is**搬到JSONConfig里面

@mek1986
Copy link
Contributor

mek1986 commented Oct 9, 2024

@yhzdys 你有什么好的想法吗?

解决循环依赖,一般是加个中间类来解决(没什么是加中间层解决不了的😂)

ObjectReaderProvider用到了JSONFactory的方法有6个

  • getProperty
  • isDisableReferenceDetect
  • isDisableArrayMapping
  • isDisableJSONB
  • isDisableAutoType
  • isDisableSmartMatch

我的想法是可以独立出一个JSONConfig类,把JSONFactory里的DEFAULT_PROPERTIES和is**搬到JSONConfig里面

@oldratlee @wenshao JSONFactory和ObjectReaderProvider,这2个类在静态初始化的时候,存在循环依赖,导致并发时死锁。

@wenshao wenshao added this to the 2.0.54 milestone Oct 10, 2024
@wenshao wenshao added the fixed label Oct 22, 2024
@wenshao
Copy link
Member

wenshao commented Oct 22, 2024

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2/2.0.54-SNAPSHOT/
@yhzdys 问题已修复,请帮忙用2.0.54-SNAPSHOT验证,2.0.54版本预计在11月底发布。

@yhzdys
Copy link
Author

yhzdys commented Oct 22, 2024

@wenshao 本地验证通过

@wenshao
Copy link
Member

wenshao commented Jan 12, 2025

https://github.com/alibaba/fastjson2/releases/tag/2.0.54
问题已修复,请用新版本

@wenshao wenshao closed this as completed Jan 12, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working fixed
Projects
None yet
Development

No branches or pull requests

3 participants