Java builder模式的线程安全实现

Java builder模式的线程安全实现,java,multithreading,design-patterns,Java,Multithreading,Design Patterns,在我寻找一个好的实践方法时,我遇到了一个问题 在这篇文章中,作者提到了一些引起我注意的事情。该模式是线程安全的 build()方法的第一个变体是线程安全的: public User build() { User user = new user(this); if (user.getAge() > 120) { throw new IllegalStateException("Age out of range"); // 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;
}
鉴于,本条并非:

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);
}
尽管如此,我认为更好的方法是在setters中抛出
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");
}
void setAge(int age)抛出非法状态异常{
如果(年龄>0&&age<120),则此年龄=年龄;
否则抛出新的非法状态例外(“年龄超出范围”);
}

我的方法仍然是线程安全的吗?若否,原因为何?以线程安全的方式实现builder模式的最佳方法是什么?

您的建议是线程安全的,因为返回的
用户
对象的值在合法范围内,而“坏”示例不能保证这一点。如果构建器有非法值,您的示例将返回null而不是抛出异常,这可能是您想要的,也可能不是您想要的


通常不希望从多个线程访问生成器对象。然而,在这种情况下,使用线程安全代码总是比较容易的;我可以想象不寻常的情况,人们实际上希望构建器由多个线程填充。当然,在这些情况下,对构建器的访问需要适当地同步,例如通过使用可变变量。

根据我的经验,假设构建器模式对大多数实现都是可能的,那么要使其线程安全,没有简单的方法

构建器模式的本质意味着在安装程序调用之间应该保持值,直到调用构建方法为止,甚至可能更久。因此,任何共享构建器的线程都可能在调用build方法之前更改值,从而导致除最后一个线程之外的所有线程的结果都不正确

我不能确定您的解决方案的最佳方法是什么,但我已经成功地在线程客户端中实现了构建器模式,每次需要构建器时,我都会引入一个工厂来创建一个新的构建器,并且只使用构建器作为局部变量,这将它保留在一个单独的堆栈上

我发现这是最简单、最安全、最有效的方法


记住,让它变得简单,如果你在例子中跳过箍圈,我会把它当作一种气味,并考虑一种新的方法。

为什么这是一个问题?为什么两个线程同时使用同一个生成器?或者根本没有?究竟为什么要在线程之间共享构建器?这些代码都不是线程安全的。博主不知道他在说什么。要实现线程安全,必须在java.util.concurrent或其子包中使用
volatile
synchronized
,或一个或多个类。@SleimanJneidi许多线程可能会尝试同时创建用户,不是吗?问题是某个对象是否是“构建器”与是否“线程安全”的问题完全正交。设计线程安全的
FooBuilder
类的规则与设计线程安全的
AnythingElse
类的规则没有区别。不过,正如其他人已经指出的那样,如果您认为您需要一个线程安全的构建器,那么您正在做一些令人惊讶的事情。(谷歌“最小惊喜原则”)