Reputation: 257
I'm building a program that requires the construction of some objects that require such intense computation to create, my smartest course would be to have them built in their own dedicated threads, while the master thread keeps grinding away on other things until the objects are needed.
So I thought about creating a special class specifically designed to create custom objects in their own thread. Like so:
public abstract class DedicatedThreadBuilder<T> {
private T object;
public DedicatedThreadBuilder() {
DedicatedThread dt = new DedicatedThread(this);
dt.start();
}
private void setObject(T i) {
object = i;
}
protected abstract T constructObject();
public synchronized T getObject() {
return object;
}
private class DedicatedThread extends Thread {
private DedicatedThreadBuilder dtb;
public DedicatedThread(DedicatedThreadBuilder builder){
dtb = builder;
}
public void run() {
synchronized(dtb) {
dtb.setObject(dtb.constructObject());
}
}
}
}
My only concern is that this mechanism will only work properly if the master thread (i.e. the thread that constructs the DedicatedThreadBuilder) has a synchronized lock on the DedicatedThreadBuilder until it's construction is completed, and therefore blocks the DedicatedThread's attempt to build the product object until it has finished construction of the DedicatedThreadBuilder. Why? Because the subclasses of DedicatedThreadBuilder will no doubt need to be constructed with parameters the will need to be passed into their own private storage, so that they can be used in the constructObject() process.
e.g.
public class JellybeanStatisticBuilder extends DedicatedThreadBuilder<JellybeanStatistics> {
private int greens;
private int blacks;
private int yellows;
public JellybeanStatisticBuilder(int g, int b, int y) {
super();
greens = g;
blacks = b;
yellows = y;
}
protected JellybeanStatistics constructObject() {
return new JellybeanStatistics(greens, blacks, yellows);
}
}
This will only work properly if the object is blocked to other threads until after it is completely constructed. Otherwise, the DedicatedThread might try to build the object before the necessary variables have been assigned.
So is that how Java works?
Upvotes: 1
Views: 157
Reputation: 59144
The problem is that you are using the subclass before it is constructed. It doesn't really have anything to do with multithreading. If you were calling constructObject
directly from the DedicatedThreadBuilder
constructor, it would be just as bad.
The reasonable implementation that is closet to what you have is just to provide DedicatedThreadBuidler
with a separate start()
method that should be called after the object is constructed.
Or you could have it extend Thread
and use the Thread methods.
Or you could have it implement Runnable
so you could use it with a Thread
or an Executor
or whatever.
Upvotes: 0
Reputation: 64837
I think what you want is to have some sort of synchronised factory class:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public class SyncFactory<T> {
// alternatively, use newCachedThreadPool or newFixedThreadPool if you want to allow some degree of parallel construction
private ExecutorService executor = Executors.newSingleThreadExecutor();
public Future<T> create() {
return executor.submit(() -> {
return new T();
});
}
}
Now you'd replace usages of T
that may need to happen before T
is ready with Future<T>
, and have a choice between calling its Future.get()
method to block until it's ready, set a timeout, or to call Future.isDone()
to check up on the construction without blocking. In addition, instead of polling the Future, you may want to have the factory call a callback or post an event to notify the main thread when it has completed construction.
Upvotes: 2
Reputation: 1977
If (big if) this is truly needed (think that one over first)...
The overall idea you are heading toward can work, but your code is confusing and, at first glance by me anyway, appears that it might not work. This type of complexity which can break things is a very good reason to double-think and even triple-think heading down this path.
The major problem I spotted right away is that you are only ever creating 1 instance of the object. If this is a factory which just creates things on another thread, then the DedicatedThread
should be called upon in DedicatedThreadBuilder
's constructObject
, not in its constructor.
If, on the other hand, you actually intend for the DedicatedThreadBuilder
to only create 1 instance of T
, then this abstraction seems unnecessary... just move DedicatedThread
's behavior out to DedicatedThreadBuilder
, as DedicatedThreadBuilder
doesn't really seem to be doing anything extra.
Second, a minor thing that isn't incorrect so much as it is just unnecessary: you have an inner class which you pass an instance of the outer class to its constructor (that is, DedicatedThread
's constructor takes a reference to its parent DedicatedThreadBuilder
). This is unnecessary, as non-static inner classes are already linked to their outer classes, so the inner class can reference the outer class without any extra reference to it.
Third, if you move the behavior out of the constructor and into a separate method, then you could synchronize that. Personally, I would have had the constructObject
be the thing that kicked off the process, so that calling dtb.constructObject()
started the object's creation, and constructObject
itself set object = newlyCreatedThing
when it was done. Then you could synchronize that method if you want, or do whatever, and not have to worry about the constructor possibly not behaving how you want - in my opinion you should not generally have to worry that a constructor might have some odd side effects.
Fourth, do you have any way to know when the object is ready and available for getting? You might want to add some mechanism for that, such as an observer or other callback.
Upvotes: 0