[英]Thread-safe implementation of builder pattern
在文章中,作者提到了一些引起我注意的事情。 该模式是线程安全的。
build()
方法的第一个变体是线程安全的:
public User build() {
User user = new user(this);
if (user.getAge() > 120) {
throw new IllegalStateException("Age out of range"); // thread-safe
}
return user;
}
鉴于此不是:
public User build() {
if (age > 120) {
throw new IllegalStateException("Age out of range"); // bad, not thread-safe
}
// This is the window of opportunity for a second thread to modify the value of age
return new User(this);
}
虽然,我认为更好的方法是在setter中抛出IllegalStateException
:
public User build() {
User u = null;
try {
u = new User(this);
}
catch(IllegalStateException e){
e.printStackTrace();
}
return u;
}
构造函数如下所示:
private User(UserBuilder builder)
{
setAge(builder.age);
}
设置者是:
void setAge(int age) throws IllegalStateException {
if(age > 0 && age < 120) this.age = age;
else throw new IllegalStateException("Age out of range");
}
我的方法仍然是线程安全的吗? 如果没有,为什么? 以线程安全的方式实现构建器模式的最佳方法是什么?
您的建议是线程安全的,即返回的User
对象将具有合法范围内的值,“坏”示例无法保证该值。 如果构建器具有非法值(您可能想要或可能不想要),则您的示例确实返回null而不是引发异常。
人们通常不希望从多个线程访问构建器对象。 但是,在这种情况下,使用线程安全代码总是容易的,这总是更好。 我可以想象异常情况,其中实际上是希望构建器由多个线程填充。 当然,在那些情况下,例如,通过使用易失性变量,需要适当地同步对构建器的访问。
根据我的经验,假设即使对于大多数实现来说,也没有简便的方法可以使构建器模式线程安全。
构建器模式的性质意味着,应该在设置调用之间保留值,直到调用build方法为止,并且可能会超出。 因此,任何共享构建器的线程都可能在调用build方法之前更改值,从而导致除最后一个线程之外的所有线程都得到错误的结果。
我无法确定您的解决方案是最好的方法,但是我已经通过在每次需要构建器时引入一个工厂来创建新的构建器,并且仅使用该构建器作为局部变量,从而成功地在线程客户端中实现了构建器模式在单独的堆栈上。
我发现这是最简单,最安全和最有效的方法。
记住要保持愚蠢,如果您像示例中那样跳过篮球,那么我会将其视为一种气味并考虑采用新方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.