Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 5 of 6
In this case, suppose we made the convertDate object the lock. The code fragment using the objects would then need to be changed to the following:
// Get usage of the owned objects.
synchronized (convertDate) {
// Get the default string for time.
long time = ...;
convertDate.setTime(time);
convertBuffer.setLength(0);
StringBuffer output =
dateFormatter.format(convertDate, convertBuffer, convertField);
String display = output.toString();
}
If the code using the owned objects is required to be single threaded, there's no need to bother with the locking step. However, adding the locking can give some additional flexibility.
For instance, the example we've been following uses instance variables for the owned objects, so that there's one set of objects for each instance of the containing class. This approach makes sense when there's heavy use of the owned objects, or when the objects may need to be configured differently for each instance of the containing class. This would be the case, for example, if we wanted to have the format string for our date example specified to the constructor of the class, instead of always using the default format.
In cases where usage of the owned objects is not extremely heavy, and they don't need to be customized for each instance of
the containing class, it may be better to have them owned by the class itself rather than have a copy for every instance of
the class. To do this, just make the member variables static:
...
// Allocate dedicated time formatting objects as member variables.
// (Synchronize on convertDate to use any or all of the objects.)
private static final Date convertDate = new Date();
private static final DateFormat convertFormat = DateFormat.getDateInstance();
private static final StringBuffer convertBuffer = new StringBuffer();
private static final FieldPosition convertField = new FieldPosition(0);
...
// Get usage of the owned objects.
synchronized (convertDate) {
// Get the default string for time.
long time = ...;
convertDate.setTime(time);
convertBuffer.setLength(0);
StringBuffer output =
dateFormatter.format(convertDate, convertBuffer, convertField);
String display = output.toString();
}
...
This approach gives the speed advantage of dedicated object reuse while sharing the memory overhead across all instances of the class.
Free object pools represent another form of object reuse. With the free pool approach, code using objects of the pooled type must track usage and explicitly return the objects to the free pool when usage is complete. The free pool keeps a collection of objects available for reuse, adding returned objects to the collection. When an object is needed, the free pool removes one from the available collection and reinitializes it, rather than constructing a new object. The free pool only constructs a new object of the pooled type when the available collection is empty.
The bookkeeping overhead of maintaining a collection of available objects limits the performance gain of this approach, but it can still be useful in circumstances where there's a lot of reuse of a particular object type. We'll look at alternative approaches to handling this bookkeeping and see how each performs in practice in order to get a better idea of when each approach might be useful.