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.
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:
Not use a separate Thread in the Constructor.
Or join the thread inside the Constructor ( which somewhat defeates the Thread's purpose ).
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!
Related
I am trying to dynamically update the keys in a HashMap.
I have created an instance of a class and set the key to get the value from the class.
I am trying to get the value of the key to change when I update the value in the class. The program I am trying to do this for has multiple large hashmaps. However, I have simplified it to the example below.
Main class
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> hashMap = new HashMap<>();
OtherClass otherClass = new OtherClass(5);
hashMap.put(otherClass.getId(), "a string");
otherClass.setId(0); // update value in class
System.out.println(hashMap.keySet()); // outputs 5 not 0
}
}
Other class
class OtherClass {
int id;
OtherClass (int id) {
this.id = id;
}
void setId(int id) {
this.id = id;
}
int getId() {
return id;
}
}
When I update the value in the class the key in the HashMap does not change.
Is what I'm trying to do even possible and if not how could I achieve this?
If you want the Map to auto-update when the id of an OtherClass object is modified, then you need to write the code for that yourself.
Unless the map and the object is tightly coupled, you should keep the logic decoupled, e.g. by implementing property change tracking.
I would recommend building it around the PropertyChangeSupport class in the Java Runtime Library.
OtherClass
First you need to enable property change tracking.
I added name property to improve test code output at end of this answer.
public final class OtherClass {
private final transient PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private int id;
private String name;
public OtherClass(int id, String name) {
this.id = id;
this.name = name;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
public int getId() {
return this.id;
}
public void setId(int id) {
int oldId = this.id;
this.id = id;
this.pcs.firePropertyChange("id", oldId, id);
}
public String getName() {
return this.name;
}
public void setName(String name) {
String oldName = this.name;
this.name = name;
this.pcs.firePropertyChange("name", oldName, name);
}
#Override
public String toString() {
return "OtherClass[" + this.id + ", " + this.name + "]";
}
}
OtherMap
Next you need to encapsulate the Map so the property change listener can be correctly handled.
To prevent memory leaks, it's important to clear() the OtherMap when it is no longer needed, otherwise a reference to a single OtherMap object that is in the OtherMap will keep the map and all the objects in the map alive in memory. To help with that, I made the object AutoCloseable, so it could be used with a try-with-resources statement, and to make code analyzers help highlight the need to close/clear the map.
final class OtherMap implements AutoCloseable {
private final PropertyChangeListener listener = this::onPropertyChange;
private Map<Integer, OtherClass> map = new HashMap<>();
public OtherMap() {
}
public Set<Integer> keys() {
return Collections.unmodifiableSet(this.map.keySet());
}
public Collection<OtherClass> values() {
return Collections.unmodifiableCollection(this.map.values());
}
public OtherClass get(int id) {
return this.map.get(id);
}
public OtherClass add(OtherClass other) {
OtherClass prev = this.map.put(other.getId(), other);
if (prev != null)
prev.removePropertyChangeListener(this.listener);
other.addPropertyChangeListener(this.listener);
return prev;
}
public OtherClass remove(int id) {
OtherClass other = this.map.remove(id);
if (other != null)
other.removePropertyChangeListener(this.listener);
return other;
}
public void clear() {
this.map.values().forEach(other -> other.removePropertyChangeListener(this.listener));
this.map.clear();
}
private void onPropertyChange(PropertyChangeEvent evt) {
if (! "id".equals(evt.getPropertyName()))
return;
Integer oldId = (Integer) evt.getOldValue();
Integer newId = (Integer) evt.getNewValue();
if (oldId.equals(newId))
return;
OtherClass other = (OtherClass) evt.getSource();
if (this.map.putIfAbsent(newId, other) != null)
throw new IllegalStateException("Duplicate key");
if (! this.map.remove(oldId, other)) {
this.map.remove(newId);
throw new IllegalStateException();
}
}
#Override
public String toString() {
return this.map.toString();
}
#Override
public void close() {
clear();
}
}
Test
OtherClass eeny = new OtherClass(3, "Eeny");
OtherClass meeny = new OtherClass(5, "Meeny");
OtherClass miny = new OtherClass(7, "Miny");
OtherClass moe = new OtherClass(9, "Moe");
OtherMap otherMap = new OtherMap();
otherMap.add(eeny);
otherMap.add(meeny);
otherMap.add(miny);
otherMap.add(moe);
System.out.println("Before: " + otherMap);
meeny.setId(2);
otherMap.remove(miny.getId());
miny.setId(4);
System.out.println("After: " + otherMap);
Output
Before: {3=OtherClass[3, Eeny], 5=OtherClass[5, Meeny], 7=OtherClass[7, Miny], 9=OtherClass[9, Moe]}
After: {2=OtherClass[2, Meeny], 3=OtherClass[3, Eeny], 9=OtherClass[9, Moe]}
I was reading about the pattern here:
https://www.geeksforgeeks.org/builder-pattern-in-java/
the last part demonstrates how to use the pattern, I tried to copy the code into my IDE & run it but it returns null,
the 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;
}
}
// Client Side Code
class StudentReceiver {
// volatile student instance to ensure visibility
// of shared reference to immutable objects
private volatile Student student;
public StudentReceiver() {
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
student = Student.Builder.newInstance().setId(1).setName("Ram").setAddress("Noida").build();
System.out.println(student.toString());
}
});
Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
student = Student.Builder.newInstance().setId(2).setName("Shyam").setAddress("Delhi").build();
System.out.println(student.toString());
}
});
t1.start();
t2.start();
}
public Student getStudent() {
return student;
}
}
// Driver class
public class BuilderDemo {
public static void main(String args[]) {
StudentReceiver sr = new StudentReceiver();
System.out.println("sr " + sr.getStudent());
}
}
When I remove the threads and run it without them it works, anyone has an idea of why it returns null instead of one of the student objects ?
In your code, main thread is executed before student receiver executes the code.
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
student = Student.Builder.newInstance().setId(1).setName("Ram").setAddress("Noida").build();
System.out.println(student.toString());
}
});
t1.start(); // It starts, but the runnable itself has not run yet!
So first you get the student which is null and print it in main, then StudentReceiver will initialize your student.
Advise, do not do it in real world tasks, but do it for learning!
Alternatively, you can wait until the user is initialized. This is one approach.
try {
t1.join();
t2.join();
} catch (Exception e) {
throw new RuntimeException("Handle it properly.", e);
}
I want to make a counter for the ID, and construct a new Person object with that ID.
My lombok class:
package nl.SBDeveloper.Persons.Lombok;
import lombok.Data;
#Data
public class Person {
private int id;
private String name;
}
My code:
Person person = new Person();
What is the best way to create this?
Define a static field. #Data creates a constructor using only the required arguments. ID is not required since it's already assigned, so you get a constructor which just takes a name.
#Data
public class Person {
private static final AtomicInteger currentId = new AtomicInteger();
private final int id = currentId.incrementAndGet();
private final String name;
}
Usage:
Person bob = new Person("Bob");
Define your data class:
public class Person {
private int id;
private String name;
Person(int id, String name) {
this.id = id;
this.name = name;
}
public String getName() {
return this.name;
}
public int getId() {
return this.id;
}
}
You can keep track of how many Person objects you create by defining a PeopleFactory object, and giving it a static personCount field. To make sure this counter is thread-safe, you would need to synchronize the field, or synchronize the method in charge of Person creation.
public class PersonFactory {
private static int personCount = 0;
public PersonFactory() {
}
public synchronized Person getPerson(String name) {
personCount++;
return new Person(personCount, name);
}
}
Testing our implementation:
public class Main {
public static void main(String[] args) {
PersonFactory personFactory = new PersonFactory();
Person bill = personFactory.getPerson("Bill");
System.out.println("ID: " + bill.getId() + ", Name: " + bill.getName());
}
}
ID: 1, Name: Bill
I have One Inner Class and One Outer Class. Using Java Reflection I want to access the data of the inner class instance.
public class OuterClass {
public OuterClass() {
super();
}
public OuterClass(InnerClass innerClass1, InnerClass innerClass2) {
super();
this.innerClass1 = innerClass1;
this.innerClass2 = innerClass2;
}
private InnerClass innerClass1;
private InnerClass innerClass2;
public class InnerClass {
public InnerClass() {
super();
}
public InnerClass(int id, String name, String rollNo) {
super();
this.id = id;
this.name = name;
this.rollNo = rollNo;
}
private int id;
private String name;
private String rollNo;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
public InnerClass getInnerClass1() {
return innerClass1;
}
public void setInnerClass1(InnerClass innerClass1) {
this.innerClass1 = innerClass1;
}
public InnerClass getInnerClass2() {
return innerClass2;
}
public void setInnerClass2(InnerClass innerClass2) {
this.innerClass2 = innerClass2;
}
}
Main Class:-
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass1 = outerClass.new InnerClass(1, "Iftekhar", "1234");
OuterClass.InnerClass innerClass2 = outerClass.new InnerClass(2, "Ahmed", "123");
outerClass.setInnerClass1(innerClass1);
outerClass.setInnerClass2(innerClass2);
Field[] fields = outerClass.getClass().getDeclaredFields();
for (Field f : fields) {
Method method = OuterClass.InnerClass.class.getMethod("getId", null);
int id = (int) method.invoke(f, null);
System.out.println(id);
}
}
}
I am anticipating the output to be 1 and 2. But i am getting the below Exception:-
Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
I am instantiating the inner Class attributes using the way show above.Can anyone please help where i am doing wrong.
You are calling getId() on a java.lang.reflect.Field instance. And a java.lang.reflect.Field is not an instance of OuterClass.InnerClass.
To fix this, you first have to get the value of the field and call getId() on that:
Field[] fields = outerClass.getClass().getDeclaredFields();
// We only have to find the method once and can reuse it
Method method = OuterClass.InnerClass.class.getMethod("getId");
// We have to call .setAccessible because the fields are private
AccessibleObject.setAccessible(fields, true);
for (Field f : fields) {
OuterClass.InnerClass value = (OuterClass.InnerClass) f.get(outerClass);
// At this point, you could also use value.getId();
int id = (int) method.invoke(value);
System.out.println(id);
}
public class OuterClass implements Serializable {
int id;
ArrayList<InnerClass> listofInner;
public int getId() {
return id;
}
public void setId(int num) {
this.id = num;
}
public ArrayList<InnerClass> getListofInner() {
return listofInner;
}
public void setListofInner(ArrayList<InnerClass> list) {
this.listofInner = list;
}
public static class InnerClass implements Serializable {
String streetno;
private List<InnerClass> children = new ArrayList<InnerClass>();
public void setStreetno(String streetno) {
this.streetno = streetno;
}
public String getStreetno() {
return streetno;
}
public List<InnerClass> getChildren() {
return children;
}
public void setChildren(List<InnerClass> children) {
this.children = children;
}
}
}
OuterClass us = new OuterClass();
us.setId(111);
//Inner
OuterClass.InnerClass ic = new OuterClass.InnerClass();
ic.setStreetno("My Street");
ic.getChildren().add(new OuterClass.InnerClass());
ArrayList<OuterClass.InnerClass> ar = new ArrayList<OuterClass.InnerClass>();
ar.add(ic);
us.setListofInner(ar);
//DF
Encoder<OuterClass> outerClassEncoder = Encoders.bean(OuterClass.class);
Dataset<OuterClass> ds = spark.createDataset(Collections.singletonList(us), outerClassEncoder);
In the above InnerClass class has property
List children = new ArrayList(); this is a type ofInnerClass list. Because of this when I am trying to covert this Custom Object to Dataset. I am getting error
java.lang.StackOverflowError: null
at sun.reflect.generics.reflectiveObjects.TypeVariableImpl.equals(TypeVariableImpl.java:189).
If I remove the children property from InnerClass. It works fine.
So Any one please help me..:)