kha*_*set 6 java multithreading final volatile immutability
我有一个案例,当我想避免防御性副本,对于可能仍然被修改的数据,但通常只是阅读,而不是写入.所以,我想使用不可变对象,使用函数mutator方法,这是常见的(java lombok能够或多或少地自动执行).我进行的方式如下:
public class Person {
private String name, surname;
public Person(String name, String surname) {....}
// getters...
// and instead of setters
public Person withName(String name) {
Person p= copy(); // create a copy of this...
p.name= name;
return p;
}
public Person copy() {....}
}
Run Code Online (Sandbox Code Playgroud)
因此,要获得具有不同名称的人的副本,我会打电话
p= new Person("Bar", "Alfred");
...
p= p.withName("Foo");
Run Code Online (Sandbox Code Playgroud)
在实践中,对象相当大(我最终使用序列化来避免编写复制代码的负担).
现在,在浏览网页时,我发现这个实现存在潜在的并发问题,因为我的字段不是最终的,因此,并发访问可能会看到返回的副本,例如,没有新的名称更改(因为没有保证在这种情况下的操作顺序).
当然,我无法使用当前实现将我的字段设为最终,因为我先复制,然后更改副本中的数据.
所以,我正在为这个问题寻找一个好的解决方案.
我可能会使用volatile,但我觉得这不是一个好的解决方案.
另一种解决方案是使用构建器模式:
class PersonBuilder {
String name, surname; ....
}
public class Person {
private final String name, surname;
public Person(PersonBuilder builder) {...}
private PersonBuilder getBuilder() {
return new PersonBuilder(name, surname);
}
public Person withName(String name) {
PersonBuilder b= getBuilder();
b.setName(name);
return new Person(b);
}
}
Run Code Online (Sandbox Code Playgroud)
这里有什么问题,最重要的是,是否有更优雅的方式做同样的事情?
我建议您看一下 Guava 的不可变集合,例如不可变列表以及它们如何从构建器创建列表等。
习语如下:
List<String> list1 = ImmutableList.of("a","b","c"); // factory method
List<String> list2 = ImmutableList.builder() // builder pattern
.add("a")
.add("b")
.add("c")
.build();
List<String> list3 = ... // created by other means
List<String> immutableList3 = ImmutableList.copyOf(list3); // immutable copy, lazy if already immutable
Run Code Online (Sandbox Code Playgroud)
我真的很喜欢上面的成语。对于实体构建器,我将采取以下方法:
Person johnWayne = Person.builder()
.firstName("John")
.lastName("Wayne")
.dob("05-26-1907")
.build();
Person johnWayneClone = johnWayne.copy() // returns a builder!
.dob("06-25-2014")
.build();
Run Code Online (Sandbox Code Playgroud)
copy()这里的构建器可以通过返回个人构建器的方法或类上的静态方法Person(建议使用私有构造函数)从现有实例获取。
请注意,上面模拟了一点 Scala 的案例类,您可以从现有实例创建副本。
最后,不要忘记遵循不可变类的准则: