【问题标题】:Thread safe Builder Pattern线程安全构建器模式
【发布时间】:2020-07-02 19:42:30
【问题描述】:

我想让我的 Builder 模式成为线程安全的但是面对这方面的问题,下面是我的代码:

// Server Side Code 
final class Student { 
  
    // final instance fields 
    private final int id; 
    private final String name; 
    private final String address; 
  
    public Student(Builder builder) 
    { 
        this.id = builder.id; 
        this.name = builder.name; 
        this.address = builder.address; 
    } 
  
    // Static class Builder 
    public static class Builder {
  
        /// instance fields 
        private int id; 
        private String name; 
        private String address; 
  
        public static Builder newInstance() 
        { 
            return new Builder(); 
        } 
  
        private Builder() {} 
  
        // Setter methods 
        public Builder setId(int id) 
        { 
            this.id = id; 
            return this; 
        } 
        public Builder setName(String name) 
        { 
            this.name = name; 
            return this; 
        } 
        public Builder setAddress(String address) 
        { 
            this.address = address; 
            return this; 
        } 
  
        // build method to deal with outer class 
        // to return outer instance 
        public Student build() 
        { 
            return new Student(this); 
        } 
    } 
  
    @Override
    public String toString() 
    { 
        return "id = " + this.id + ", name = " + this.name +  
                               ", address = " + this.address; 
    } 
} 
  


----------

还有一个名为 StudentReceiver.java 的类,我在其中使用了多线程:

    class StudentReceiver {

    // volatile student instance to ensure visibility
    // of shared reference to immutable objects
    private volatile Student student;

    public StudentReceiver() throws InterruptedException {

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                student = Student.Builder.newInstance().setId(1).setName("Ram").setAddress("Noida").build();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                student = Student.Builder.newInstance().setId(2).setName("Shyam").setAddress("Delhi").build();
            }
        });

        t1.start();
        t2.start();
        //t1.join();
        //t2.join();
        
    }

    public Student getStudent() {
        return student;
    }
}


----------

下面是我调用这些方法的主类:

//Driver class 
public class BuilderDemo { 
 public static void main(String args[]) throws InterruptedException 
 { 
     for(int i=0; i<10;i++)
     {
         StudentReceiver sr = new StudentReceiver();
         
         System.out.println(sr.getStudent());
     }
 } 
}


----------

我得到的输出如下:

null
null
null
null
null
null
null
null
id = 1, name = Ram, address = Noida
null

为什么我在这里得到空值? 谁能解释一下如何使 Builder Pattern 线程安全,以便它可以在多线程环境中使用。

【问题讨论】:

  • StudentReceiver 代码对我来说毫无意义。你能解释一下你为什么要在那里做你正在做的事情吗?
  • “为什么我在这里得到空值?” - 因为您在设置之前访问该字段。你的 Builder 没问题。构造函数在完成其工作之前不应返回。

标签: java design-patterns


【解决方案1】:

您的构建器模式不是这里的问题。 StudentReceiver 的构造函数是。

在其中启动一个线程而不加入它会导致对象被分配,可能而且可能在线程启动之前。所以student 字段在相当长的一段时间内都不会设置。事实上,在构造函数之后执行System.out.println(sr.getStudent()); 行会(很可能)从getStundent() 接收null

解决办法是:

  • 不在构造函数中使用单独的线程。
  • 或者在构造函数中加入线程(这有点违背线程的目的)。

而且 Builder 类应该不是静态的

这是我要做的一个例子:

public interface IBuilder
{
   IBuilder setId( int id );
   // ...
   Student build();
}

final class Student { 
  
    // final instance fields 
    private final int id; 
    // + other fields - left out for brevity
  
    private Student(Builder builder) 
    { 
        this.id = builder.id; 
        // + other fields
    } 

    private static Object builderLock = new Object();
    public static IBuilder getBuilder()
    {
        synchronized(builderLock)
        {
            return new Builder();
        }
    }
  
    // Static class Builder 
    public class Builder implements IBuilder {
  
        // instance fields 
        private int id = -1; 
        // ...  

        private Builder() {} 
  
        // Setter methods 
        public IBuilder setId(int id) { 
            this.id = id; 
            return this; 
        }

        public Student build() {
            return new Student(this);
        }
    }
}

免责声明:未经测试!

【讨论】:

  • 你能重现最后两行 OP 输出吗? 8 次null,然后设置,然后再次null
  • @Lino-Votedon'tsayThanks 不,我不能。那只是机会。在任何其他机器上,它的行为可能与他的不同。
  • 我添加了更多输出here
  • 如果您有其他信息,请将其添加到您的问题中。
  • geeksforgeeks.org/builder-pattern-in-java我已经从这里复制了它
猜你喜欢
  • 2014-02-17
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多