简体   繁体   English

生成器模式的线程安全实现

[英]Thread-safe implementation of builder pattern

There is an article that I came across while I was looking for a good practice of builder pattern . 我在寻找构建器模式的良好实践时遇到了一篇文章

In the article, the author mentions about something that drew my attention. 在文章中,作者提到了一些引起我注意的事情。 the pattern being thread safe. 该模式是线程安全的。

The first variation of build() method is thread-safe: build()方法的第一个变体是线程安全的:

public User build() {
    User user = new user(this);
    if (user.getAge() > 120) {
        throw new IllegalStateException("Age out of range"); // thread-safe
    }
    return user;
}

Whereas, this one is not: 鉴于此不是:

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);
}

Although, I think a better approach would be throwing IllegalStateException in the setters: 虽然,我认为更好的方法是在setter中抛出IllegalStateException

public User build() {
    User u = null;
    try {
        u = new User(this);
    }
    catch(IllegalStateException e){
        e.printStackTrace();
    }
    return u;
}

where the constructor looks like this: 构造函数如下所示:

private User(UserBuilder builder)
{
    setAge(builder.age);
}

and the setter is: 设置者是:

void setAge(int age) throws IllegalStateException {
    if(age > 0 && age < 120) this.age = age;
    else throw new IllegalStateException("Age out of range");
}

would my approach still be thread-safe? 我的方法仍然是线程安全的吗? If not, why? 如果没有,为什么? And what is the best way to implement the builder pattern in a thread-safe way? 以线程安全的方式实现构建器模式的最佳方法是什么?

Your proposal is thread safe in the sense that the User object returned will have a value in the legal range, which is not guaranteed with the "bad" example. 您的建议是线程安全的,即返回的User对象将具有合法范围内的值,“坏”示例无法保证该值。 Your example does return null rather than throwing an exception if the builder had an illegal value, which may or may not be what you want. 如果构建器具有非法值(您可能想要或可能不想要),则您的示例确实返回null而不是引发异常。

One would not normally want to access a builder object from multiple threads. 人们通常不希望从多个线程访问构建器对象。 However, it's always better to use thread safe code when doing so is easy as in this case; 但是,在这种情况下,使用线程安全代码总是容易的,这总是更好。 I can imagine unusual cases where one actually would want the builder to be populated by multiple threads. 我可以想象异常情况,其中实际上是希望构建器由多个线程填充。 In those cases, of course, access to the builder would need to be properly synchronized, for example through the use of volatile variables. 当然,在那些情况下,例如,通过使用易失性变量,需要适当地同步对构建器的访问。

From my experience there is no easy way to make the builder pattern thread safe assuming it's even possible for most implementations. 根据我的经验,假设即使对于大多数实现来说,也没有简便的方法可以使构建器模式线程安全。

The nature of the builder pattern implies that values should be persisted between setup calls, right up to the build method is called and potentially beyond. 构建器模式的性质意味着,应该在设置调用之间保留值,直到调用build方法为止,并且可能会超出。 Thus any thread sharing the builder could potentially change the values before the build method is called resulting in an incorrect result for all threads but the last. 因此,任何共享构建器的线程都可能在调用build方法之前更改值,从而导致除最后一个线程之外的所有线程都得到错误的结果。

I cannot be sure for your solution what the best approach is but I have successfully implemented builder patterns in a threaded client by introducing a factory to create a new builder every time the builder is needed and only using the builder as a local variable this keeping it on a separate stack. 我无法确定您的解决方案是最好的方法,但是我已经通过在每次需要构建器时引入一个工厂来创建新的构建器,并且仅使用该构建器作为局部变量,从而成功地在线程客户端中实现了构建器模式在单独的堆栈上。

I have found this to be the simplest, safest and most efficient method. 我发现这是最简单,最安全和最有效的方法。

Remember keep it stupid simple, if you are jumping through hoops like in your example then I would treat this as a smell and consider a new approach. 记住要保持愚蠢,如果您像示例中那样跳过篮球,那么我会将其视为一种气味并考虑采用新方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM