Reputation: 29
I want to make my Builder pattern as Thread safe But facing issues in that, below is my code:
// 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;
}
}
----------
There is another class named StudentReceiver.java in which I am using multithreading:
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;
}
}
----------
Main class is below from where I am calling these methods:
//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());
}
}
}
----------
The output I am getting is like below:
null
null
null
null
null
null
null
null
id = 1, name = Ram, address = Noida
null
Why I am getting null here?? May anyone explain and How to make Builder Pattern thread safe so that it can be used in multithreaaded environment.
Upvotes: 0
Views: 442
Reputation: 16129
Your Builder Pattern is not the problem here. The Constructor of StudentReceiver
is.
Starting a Thread inside it without joing it there will lead to the object being assigned, possibly and probably before the Thread even started. So the student
Field will not be set for quite some time. So much time in fact, that executing the System.out.println(sr.getStudent());
line right after the constructor will (very probably) receive null
from getStundent()
.
The fix would be to either:
And the Builder class should not be static.
Here is an example of what I'd do:
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);
}
}
}
Disclaimer: untested!
Upvotes: 1