Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

Open
yhzdys opened this issue Sep 29, 2024 · 7 comments
Open
Labels
bug Something isn't working
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants