知识卡片

为每次请求创建新对象,是避免多线程错误的最简单隔离手段

普通读书笔记卡

内容

如果采用每会话一线程,最重要的事是给每个线程创建一个隔离区,让开发者在区内可以完全忽略多线程问题。最常用的做法是每次处理请求时都创建全新的对象,并保证这些对象不会被放到其他线程也能看到的地方(比如静态变量)——因为其他线程根本无法引用这些新对象,它们自然就是隔离的。很多开发者不愿意这样做,是因为担心创建新对象是一个昂贵的操作,于是转而缓存复用对象;但缓存对象意味着必须自己同步对它们的并发访问,反而重新把线程问题请了回来。实际上对象创建的开销和具体的虚拟机、内存管理机制强相关,在不少现代运行环境下已经非常便宜;书中特意抛出一个问题吊胃口——一台P3 600MHz机器、Java 1.3虚拟机一秒钟能创建多少个Java日期对象,暗示答案会颠覆”创建对象很贵”的直觉印象。为每个会话/请求创建新对象换来的是能规避掉大量并发错误,还有助于提升可伸缩性。这个策略并非没有例外:静态的类变量、全局变量、单子对象仍然必须同步访问;如果确实需要某种全局内存空间,应该用注册表来实现——让它表面看起来像静态变量,实际内部按线程分别存放数据。另外,有些对象本身创建代价确实很高(最典型的是数据库连接),这类对象不适合”每次都创建全新的”,而应该放进一个显式的连接池里,需要时取用、用完归还,且这套取用/归还的操作本身同样需要同步。

参考来源

- 位置:《企业应用架构模式》第一部分"表述"之"第5章 并发"之"5.7 应用服务器并发"(源文件:_epub-src/OEBPS/Text/000040.html) - 结论依据:原文说明"如果使用每会话一线程,最重要的是创建和进入一个隔离区……最常用的方法是让线程每次都创建新的对象来处理请求,以保证这些对象不会被放到其他线程可以看见它们的任何地方……为每个会话创建崭新的对象能避免许多并发错误,而且可以提高可伸缩性……即使能为会话创建新对象使得情况相对安全,有些对象的创建代价仍是比较高并因而需要另外处理——最常见的例子就是数据库连接",直接支撑本卡结论。 - 原始内容:很多开发人员非常担心新对象的创建,因为他们被告知创建新对象是一个昂贵的过程。因此,他们经常缓存对象。使用缓存的问题是必须以某种方式同步对它们的访问。