在 Java 的新 Collections 库中,除了性能之外还有其他理由牺牲线程安全吗?
3 回答
是的,还有另一个原因。使集合的每个方法同步使集合线程安全,但是
- 有更快的解决方案(如并发集合)
- 无论如何这还不够,因为您经常必须实现 check-then-act 操作(例如检查元素是否存在,如果不存在则添加它)或迭代,无论如何都需要外部同步
此外,根本没有牺牲线程安全性,因为您只需将 ArrayList(例如)包装在同步集合代理中,即可获得与旧的、默认同步的 Vector 相同的同步保证:
List<String> list = Collections.synchronizedList(new ArrayList<String>());
因此,您可以两全其美:不需要同步时的快速收集(99% 的情况),以及需要时的同步收集。
性能是主要原因。
但是,还有一个重要的哲学/设计原因:您不能仅通过使各个集合类线程安全来实现完全的线程安全。
安全并发代码通常需要不同级别的同步,例如:
- 在更高级别 - 同时锁定两个集合,以便您可以从一个集合中删除一个元素并将其添加到另一个集合)
- 在较低级别 - 锁定并发数据表中的各个项目行
所以在某种程度上,使集合类同步将是一个相当武断的决定,不适合许多(如果不是大多数)情况。因此,使集合不安全并让用户决定他们的并发策略(是否需要、适当的粒度级别、事务处理方法等)是一个更好的选择。
除此之外,另一种选择是采用 Clojure 开创的方法并使用不可变的持久集合类,这样您就不必担心锁或线程安全。但这需要对你的状态方法进行更全面的重新思考......
为什么性能不够好是一个原因?
强制线程“安全性”(无论如何对于大多数情况来说可能是不完整的——例如没有 putIfAbsent)已被删除,所以当你不需要它时你不必支付成本,但仍然可以选择制作东西当你这样做时,你自己线程安全(并且具有你选择的粒度)。
我猜想实现是没有办法在不需要程序员考虑它的自动线程安全中烘焙,可以在所有情况下使用,可靠地工作并且不会增加显着的开销。
另请注意,“甚至更新的”并发 utils 包带来了几个新的线程安全集合实现,它们用于非常特定的目的。同样,对于不同的任务,您需要不同的工具,而旧的集合类提供了一个非常差的中间地带,并不真正适合大多数应用程序。