Java - InheritableThreadLocal跨线程泄漏数据

时间:2016-07-30 23:03:26

标签: java multithreading jetty threadpool

情景:

我正在运行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?
  3. 修改

    我看到了一种行为:

    1. Thread-1在ITL中设置RequestData并继续。
    2. Thread-2在ITL中设置RequestData并继续。
    3. Thread-1执行对数据库的调用。
    4. Thread-2正在进行并修改RequestData。
    5. Thread-1唤醒并搜索RequestData,但发现设置的RequestData是Thread-2。
    6. 当一个线程执行I / O时,是否会有一些东西,可能会继续在Jetty中管理线程的方式?由于线程重用,这里是否有可能存在父子线程?

2 个答案:

答案 0 :(得分: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中可见的引用。
  5. 示例代码以证明推理:

        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();
            }
    
        }
    

答案 1 :(得分:0)

与Jetty示例相关的是,Jetty从先前的工作线程中生成新的工作线程 。因此,如果InheritedThreadLocal初始化后线程1产生了线程2,则从那时起,两个线程的reqData值都相同。 InheritedThreadLocal与Jetty结合使用似乎不安全。

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