假设我们有一个 Java 对象,它只有原始字段标记为 final,因此是不可变的。
现在有几个线程需要这个对象中包含的信息来完成它们的工作,所以有两个选择:
- 将对单个实例的引用交给所有线程。
- 为每个线程创建对象的克隆/副本。
从本质上讲,问题归结为:在内部,JVM 是否从复制对象中获得任何好处,例如因为读取变得更有效率,还是仅仅浪费内存,因为这无关紧要?
假设我们有一个 Java 对象,它只有原始字段标记为 final,因此是不可变的。
现在有几个线程需要这个对象中包含的信息来完成它们的工作,所以有两个选择:
从本质上讲,问题归结为:在内部,JVM 是否从复制对象中获得任何好处,例如因为读取变得更有效率,还是仅仅浪费内存,因为这无关紧要?
从技术上讲,如果所述线程在不同的 CPU 内核上运行,由于缓存,无论如何都会有单个对象的多个副本。因此,克隆对象并没有真正的好处,如果多个线程最终在同一个处理器上,实际上可能会损害性能。
事实上,克隆一个不可变对象似乎适得其反,因为在这种情况下,不可变性的目的是为所有线程创建一个共享的只读对象以安全使用。
当没有克隆发生时,所有的好处都会有,包括你提到的那个:只有当所有线程都读取相同的内存位置时,读取才会更有效率。例如,如果将两个线程分配给同一个内核,则其缓存可以满足两个线程的读取。
不可变对象极大地简化了您的程序,因为它们:
1.易于构建、测试和使用
2.自动线程安全,没有同步问题
3.不需要复制构造函数
4.不需要克隆的实现
5.允许hashCode使用延迟初始化,并缓存其返回值
6.用作字段时不需要防御性复制制作好的Map键和Set元素(这些对象在集合中不得更改状态)
7.它们的类不变量一经建立就建立了,再也不需要检查了
8.总是有“失败原子性”:如果一个不可变对象抛出一个异常,它永远不会处于不受欢迎或不确定的状态
使用不可变对象的全部意义在于您可以以线程安全的方式共享它们,因此手动复制/克隆它们毫无意义。
请注意,在您的情况下,例如,如果两个线程在不同的 CPU 上运行,则每个 CPU 的缓存中可能有多个字段副本。
可能不变性的主要好处是所谓的“安全发布”,这意味着您可以安全地将对象引用提供给许多线程/进程,而不必担心任何并发症。
只是给每个人相同的参考 - 克隆否定了这个好处。
这会浪费内存。创建对相同内存地址的新引用比一次又一次地复制所有信息要少得多。在这种情况下,通过克隆您不会获得任何收益,除非在极少数情况下,您将对象用作synchronized
块的锁或类似的东西。但即便如此,这将是一个奇怪的应用程序。