变量来存储某些线程上下文数据

许多图书馆使用ThreadLocal变量来存储某些线程上下文数据。通常在Web应用程序中,这个上下文是一个单一的请求(=线程)。然而,有一个警告-大多数容器使用线程池。它节省了每次实例化新线程的成本。

所以这两件事都是好的,但是当组合出现问题时--如果库不清理它的线程局部变量,它将被服务为一个包含已经填充的线程本地的线程。这方面有两个问题:

  • 内存泄漏-Tomcat被指控在重新部署时泄露信息。这是因为有许多未清理的线程局部变量,并且线程池在重新部署时不会重新创建。在Tomcat 7中,通过清除所有线程局部变量并显示警告,这是固定的。但这只是在重新部署的时候。想象一下当线程本地实际存储Map时的通常情况。对象被放入地图中,如果不进行清理,它可以无限期地生长。
  • 非确定性行为-库可能期望一个干净的线程,因此一个空线程本地意味着“我必须做一些重要的初始化”。但相反,它被提供了一个已填充的线程本地,因此不会发生初始化,取而代之的是旧的(可能不相关的)数据。

因此,清理这些线程局部变量是非常重要的。以下是三个选择:

  • ThreadLocal.remove()当代码完成它的工作时。这是一种直截了当的方法,但并不总是可能的,因为线程本地可能需要同一个请求中的库跨多个调用。
  • 提供.cleanup().close()方法-调用代码将负责清理所有使用的资源(包括线程局部变量)。许多库已经有了关闭IO资源的方法。
  • 提供一个servlet过滤器,它将在每个请求的末尾清理。这并不使库必须依赖ServletAPI,但它改进了它在Web上下文中的使用。使用Servlet3.0,过滤器甚至可以自动注册自己,因此这对用户来说是不可见的。

如果是你的代码,你就没事了。但正如我所提到的-许多图书馆都有这个问题。所以去找他们报告吧。

https://www.jianshu.com/p/ca4a07d32af9

如果您使用拒绝针对此问题采取措施的库,您还有另一种选择:首先调查(通过查看库代码,或者如果不是开源-反编译)是否会发生上述两个问题中的任何一个。(例如,如果线程局部变量仅用于存储日期格式,则不要担心)。如果确实存在问题,那么自己编写一个过滤器,清除所有线程局部变量,就像tomcat在关闭时所做的那样。看以在这篇文章中看到

Java有两种类型的异常--检查和未检查。简而言之,选中是指开发人员可以合理地从异常中恢复的情况,而未检查的异常是无法处理的编程错误释何时使用哪个。

但并不是简单的检查异常使得代码更加“丑陋”。它们迫使开发人员编写TRY/CATCH块或重新抛出异常。但是重新抛出隐藏了另一个问题--一些异常不应该跨越模块边界。当您被迫捕获检查过的异常(您不知道该如何处理)时,最常见的做法是将其包装在RuntimeException中并重新抛出它。

https://www.douban.com/note/813760999/

实际上,它可能不是最常见的--特别是新手程序员倾向于吞下异常,其中包含空的CATCH块。如果存在一些用于异常处理的通用层,则日志和重抛有时会导致堆栈加倍。无论如何,这里有许多不好的做法,导致代码很难调试和维护。

有人说,检查过的例外应该完全消除,因为他们带来的冗长,乏味和错误倾向。C#根本没有检查异常。当然,消除它们应该考虑到向后兼容性。

然而,我认为,有这两种例外的决定有其优点(ES)。这迫使开发商认为在这种情况下可能会出现例外情况,因此他必须采取措施。API声明它将抛出异常,开发人员将看到以下内容编译时。它加强了编译时的安全性。您不应该等到代码投入生产时才发现某些东西可能会失败。Javadoc?这是一个很好的选择,但我敢打赌,在异常发生之前,没有人会读javadoc。

那么,如何拥有“最好的两个世界”呢?我有个奇怪的想法为API定义两个接口(通过继承链接,这样实际上只支持一个接口),并通过工厂提供一种方法抛出检查异常的实现,或者将检查的异常包装为未检查异常的实现。可能是可行的,也可能是愚蠢的,我看不出来。现在看起来很奇怪。

但以上所述充其量不过是一种解决办法。然后又出现了另一个想法-介绍@RethrowExceptions方法注释。它将告诉编译器,在此方法中,您不希望处理检查过的异常,但也不希望声明它们被抛出。(注释的名称可以改进)。在我脑海中出现的最简单的实现中,这可以简单地告诉编译器将整个方法体围绕在try {..} catch (Exception ex) { throw new RuntimeException(ex);}。好处:

  • 编译器仍然警告您使用的方法可能引发异常,您必须考虑处理它。
  • 不必要的尝试/捕捉不会使代码变得丑陋。你也不会强迫你的来电者去想怎么处理这个例外
  • 吞咽例外的可能性减少了。

因此,简而言之,这样的注释会将一个方法标记为一个无法处理异常的方法,并且不希望将此决定传播给它的调用方。

这个想法听起来不那么奇怪。我想即使现在也可以使用编译器插件来实现它。或者,它已经在如下所示的东西中实现

posted @ 2021-09-30 16:45  javd9w  阅读(128)  评论(0)    收藏  举报