Java - InheritableThreadLocal跨线程泄漏数据

问题描述 投票:0回答:2

场景:

我正在运行Jetty服务的Java Web应用程序。我在调用API时使用InheritableThreadLocal来存储一些数据,稍后当API调用进一步的操作时(在同一个线程中)访问这些数据。我存储这些数据的方式是:

码:

public class RequestDataHolder {

    protected static final InheritableThreadLocal<RequestData> reqData = new InheritableThreadLocal<RequestData>();

    public static void setRequestData(RequestData request) {
        reqData.set(request);
    }

    public static void removeRequestData() {
        reqData.remove();
    }

    public static RequestData getRequestData() {
        return (RequestData) reqData.get();
    }

}

public class RequestData {
   private String requestId;
   private String apiStartTime;

   // getters and setters
}

问题:

  1. 当两个API调用并行发生时,我发现有时Thread-1看到了ThreadData 2设置的RequestData对象。这里是否存在数据泄漏的可能性?
  2. 我是否正确使用静态方法来设置和获取InheritableThreadLocal?

编辑

我看到一个行为:

  1. Thread-1在ITL中设置RequestData并继续。
  2. Thread-2在ITL中设置RequestData并继续。
  3. Thread-1执行对数据库的调用。
  4. Thread-2正在进行并修改RequestData。
  5. Thread-1唤醒并搜索RequestData,但发现设置的RequestData是Thread-2。

可能会有一些东西,当线程执行I / O时,可能会继续在Jetty中管理线程的方式吗?由于线程重用,是否有可能出现父子线程?

java multithreading jetty threadpool
2个回答
0
投票

最后,我想出了这个问题。

推理:

问题发生的原因是,即使Java的InheritableThreadLocal保证两个线程不会看到彼此,但是不能保证您在此变量中放置的内容不会在线程中可见。

例:

例如,对象“RequestData”存在于堆上。现在考虑这一系列事件:

  1. Thread-1在ITL上调用一个集合并设置RequestData对象
  2. Thread-2是从Thread-1生成的子线程。由于ITL的性质,Thread-2将ITL转移。
  3. Thread-2会在ITL上获取RequestData。现在,此对象将具有与步骤1中设置的对象相同的哈希代码。
  4. 因此,如果您修改了Thread-2中的任何内容,您将最终更新将在Thread-1中可见的引用。

示例代码证明推理:

    public class Test implements Runnable {

        @Override
        public void run() {
            RequestData requestData = new RequestData();
            System.out.println("Thread ID: Setting requestData with hash code:" + requestData.hashCode());
            RequestDataHolder.setRequestData(requestData);
            Thread child = new Thread(new Runnable() {
                @Override
                public void run() {
                    RequestData requestData = RequestDataHolder.getRequestData();
                    System.out.println("Thread ID: Retrieved requestData with hash code:" + requestData.hashCode());
                }
            });
            child.start();
        }

        public static void main(String[] args) {
            Thread thread = new Thread(new Test());
            thread.start();
        }

    }

0
投票

Jetty示例中的相关内容是Jetty从以前的工作线程生成新的工作线程。因此,如果Thread-1生成Thread-2,则在初始化InheritedThreadLocal之后,从该点开始,两个线程对reqData具有相同的值。似乎InheritedThreadLocal与Jetty结合使用是不安全的。

此外,由于RequestDataHolder.reqDatastatic final,如果这个类在主线程中很早就被初始化,由于InheritedThreadLocal的性质,这个引用将被复制到所有子线程。

© www.soinside.com 2019 - 2024. All rights reserved.