public class Address {
private final Long id;
private final String street;
Address(Long id, String street) {
this.id = id;
this.street = street;
}
public static AddressBuilder builder() {
return new AddressBuilder();
}
public static class AddressBuilder {
private Long id;
private String street;
AddressBuilder() {
}
public AddressBuilder id(Long id) {
this.id = id;
return this;
}
public AddressBuilder street(String street) {
this.street = street;
return this;
}
public Address build() {
return new Address(id, street);
}
public String toString() {
return "Address.AddressBuilder(id=" + this.id + ", street=" + this.street + ")";
}
}
}
//测试类
public class Test {
Address obj;
public void write() {
Address address = Address.builder()
.id(1L)
.street("street 1")
.build();
obj = address;
}
public void read() {
Long id = obj.id;
}
}
现在,线程 A 调用 write 方法,创建 Address 实例并赋值给 obj,线程 B 调用 read 方法,读取 id 值。 这样子应该不是线程安全的吧,即使 id 有 final 修饰。
现在疑惑是,使用 Builder 模式创建对象一定是线程安全的吗?
虽然理论每次调用 build 方法会创建一个新实例,各线程之间不共享该实例也就不会出现并发问题。 但是,使用 Java Bean 也可以这样子
Address address = new Address();
address.setId(99L);
address.setStreet("Street 2");
每个线程都创建一个新实例啊,即使指令重排也没有影响啊。
但是 Effective Java 中又强调使用 Builder 模式可以规避 Jave Bean 创建对象时,出现的线程不安全问题。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.