8

servlet 规范(请参阅我之前的问题)保证同一个线程将执行所有过滤器和关联的 Servlet。HttpServletRequest.setAttribute鉴于此,如果可以选择使用 a ThreadLocal(假设您正确清理),我认为使用传递数据没有任何用处。我觉得使用 有两个好处ThreadLocal:类型安全和更好的性能,因为没有使用字符串键或映射(除了可能通过(非字符串)线程 ID 进入线程集合)。

有人可以确认我是否正确,以便我可以继续放弃setAttribute吗?

4

4 回答 4

3

I'd advise against ThreadLocal.

I can see why you're considering it, but I think you're falling into the premature optimization trap. ThreadLocals are essentially global variables - rarely a good design. The time-savings will be negligible. I guarantee this will never be the bottleneck in your server's throughput.

Using ThreadLocal may also give you problems if you want to start using asynchronous responses to some requests (e.g. to support long-polling) as the threading model is quite different.

于 2012-04-11T18:23:44.063 回答
3

以这种方式使用 ThreadLocal 意味着您依赖某种类型的单例来维护共享状态。最终,这通常被认为是糟糕的设计,因为它有许多缺点,包括难以封装功能、意识到依赖关系以及无法交换(或模拟)功能。

与在单例中处理不太常见的 ThreadLocal 变量提议相比,使用属性或会话的性能开销可能是值得的。但是,这当然取决于您的特定用例和项目。由于难以与 servlet 共享应用程序状态(难以注入依赖项),在 servlet 中使用单例作为维护应用程序状态的一种方式有些常见,但这些对象单例对象维护每个用户状态(在 EJB 之外)是不常见的)。

如果使用会话或将容器对象设置为属性,您将只处理通过字符串的单个提取(这将是 O(1)),然后是容器类型的单个案例(具有特定的访问器您想要的所有值)。进一步调用代码应该适当地使用参数,并尽可能避免使用任何类型的全局或单例。

最终,在以性能的名义考虑一个有点不寻常(尽管很聪明)的解决方案之前,总是先测试简单的实现,看看它的性能是否足够。与花费在 db 查询和 tcp 连接建立上的 20+ms 相比,这里可能节省的 2ns 很可能微不足道。

于 2012-04-10T21:55:30.727 回答
3

如果您尝试为处理 HttpServletRequest 的代码“隐藏”设置“全局”变量,则线程本地会更好地工作。如果您正在使用 JSP/JSF 页面,或其他从 HttpServletRequest 读取的 Web UI 组件,那么您最终将不得不自己从 ThreadLocal 中提取信息。对于大多数网络编程来说,这 2 是不等价的。

于 2012-04-10T21:22:41.883 回答
3

ThreadLocal 比 HttpServletRequest.setAttribute(“key”, “value”) 更可取吗?

取决于具体的功能要求。

例如,JSFFacesContextThreadLocal. 这使您能够访问所有 J​​SF 工件,包括“原始”HttpServletRequestHttpServletResponse由 执行的代码中的任何位置FacesServlet,例如托管 bean。大多数其他基于 Java 的 MVC 框架都遵循相同的示例。

根据你的评论,

我主要需要将 User 和 EntityManager 对象从用户和数据库过滤器传输到 Servlet。我还发现,在后面的代码中经常和意外地需要这些,我很想在 Servlet 之外使用它们(即在 doGet 调用的嵌套代码中)。我觉得可能有更好的方法来编写更深入的代码 - 建议?

至于User我假设这是一个会话属性的示例,我宁愿遵循与 JSF 相同的方法。创建一个ThreadLocal<Context>whereContext是您的自定义包装类,其中包含对当前的引用HttpServletRequest,也许还可以HttpServletResponse让您可以在代码中的任何位置访问它们。如有必要,提供方便的方法以User直接从Context类中获取。

至于这个EntityManager例子,你可以采用同样的方法,但我个人不会把它放在同一个 ThreadLocal<Context>,而是一个不同的。或者,更好的是,只需从服务层的 JNDI 获取它,这将允许您对事务进行更细粒度的控制。在任何情况下,请绝对确保您正确处理提交/关闭。从容器中接管持久性和事务管理应该非常小心。我真的会重新考虑反对使用现有的和精心设计的 API/框架(如 EJB/JPA),否则您将冒着完全浪费时间重新发明所有已经标准化的 API 和东西的风险。

也可以看看:

于 2012-04-12T04:24:09.543 回答