2011-04-26

Simple Code Patterns in Java []

Moved to the page http://3rdstage.wikia.com/wiki/Simple_Patterns_and_Typical_Samples in my wiki as of 23th Jul. 2013.

wait, notify and notifyAll

The thread should own the monitor of the instance to call wait, notify or notifyAll before the call. So, the call of these basic method should be always located inside synchronized block. A thread can wake up by unexpected or unpredictable reason, so the call of wait and the logic to process after wake-up should be guarded by loop. So, the typical code block for calling wait is
   ...
   final Object obj = new Object;
   ...

   synchronized (obj) {
         while ()
             obj.wait(timeout);
         ... // Perform action appropriate to condition
   }

   ...
And for notifyAll
   ...
   final Object obj = new Object;
   ...

   synchronized (obj) {
      //Perform some action to release the hold condition
      obj.notifyAll();
      ...
   }

   ...

Typical code pattern for Regex API

The simplest code pattern for when a regular expression is used just once is to use static Pattern.matches method.

boolean b = Pattern.matches("a*b", "aaaaab");

When using a expression several times, it is better to use compiled pattern object considering performance.

Pattern p = Pattern.compile("a*b");
boolean b1 = p.matcher("aaaaab").matches();
boolean b2 = p.matcher("bbbbb").matches();

Lazy initialization of field

Lazy initialization of instance field using double-checked locking

Prior to JDK 1.5, double-checked locking is not safe with Java because the reordering of object publication and initialization can not be controlled. But as of JDK 1.5, volatile is specified not to be reordered, so it become possible to use safely double-checked locking with volatile field.

A typical code would be like this :

public class Worker{

   private volatile Resource resource = null; //creating resource is expensive
   private final Object lock = new Object();

   public Resource getResource(){
      Resource r = resource;
      if(r == null){ //first check
         synchronized(lock){
            r = resource;
            if(r == null){ //second check
               resource = r = new Resource();
            }
         }
      }
      return r;
   }
   
   ...
}

In above sample code, note that the lock object is define to be final, because reference on non final field can change any time.[5]

Lazy initialization of static field using initialization on demand holder class

On behalf of the characteristics of class loading and static initialization, static field can be safely lazy initialized using holder class without locking.

JVM load classes in lazy way and JVM run static initilizers at class initilization time which is after class loading but before the class is used by any thread. So, the static holder class having static field like the below would be lazy initialized in safe way.

public class Worker{

   private static class ResourceHolder{
      public static Resource resource = new Resource();
   }

   public static Resource getResource(){
      return ResourceHolder.resource;
   }
   
   ...
}

More readings
  1. Double-checked locking in Wikipedia
  2. The "Double-Checked Locking is Broken" Declaration
  3. Synchronization and the Java Memory Model by Doug Lea
  4. "Item 71 : Use lazy initialization judiciously" of Effective Java, 2nd Ed.
  5. How Synchronization works in Java ? by Javin Paul
More codes
CommonAnnotationBeanPostProcessor.findResourceMetadata method of Spring framework

The following code from Spring framework uses double-checked locking for non-volatile field.

Is it safe ?

Assuming ConcurrentHashMap.put method may not be so complicated to expose stale values to threads and the local codes inside second check block (inner if clause checking metadata == null) would not be reordered with ConcurrentHashMap.put method execution, the below code could be almost safe ?

package org.springframework.context.annotation;

public class CommonAnnotationBeanPostProcessor extends ...{

  ...
  private transient final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
  new ConcurrentHashMap<Class<?>, InjectionMetadata>();

  ...

  private InjectionMetadata findResourceMetadata(final Class clazz) {
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
    if (metadata == null) {
      synchronized (this.injectionMetadataCache) {
        metadata = this.injectionMetadataCache.get(clazz);
        if (metadata == null) {
          final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
          ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
            public void doWith(Field field) {
              if (webServiceRefClass != null 
                  && field.isAnnotationPresent(webServiceRefClass)) {
                if (Modifier.isStatic(field.getModifiers())) {
                  throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
                }
                newMetadata.addInjectedField(new WebServiceRefElement(field, null));
              }
              else if (ejbRefClass != null 
                  && field.isAnnotationPresent(ejbRefClass)) {
                if (Modifier.isStatic(field.getModifiers())) {
                  throw new IllegalStateException("@EJB annotation is not supported on static fields");
                }
                newMetadata.addInjectedField(new EjbRefElement(field, null));
              }
              else if (field.isAnnotationPresent(Resource.class)) {
                if (Modifier.isStatic(field.getModifiers())) {
                  throw new IllegalStateException("@Resource annotation is not supported on static fields");
                }
                if (!ignoredResourceTypes.contains(field.getType().getName())) {
                  newMetadata.addInjectedField(new ResourceElement(field, null));
                }
              }
            }
          });
          ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
            public void doWith(Method method) {
              if (webServiceRefClass != null 
                  && method.isAnnotationPresent(webServiceRefClass) 
                  &&
                  method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
                }
                if (method.getParameterTypes().length != 1) {
                  throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
                }
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
                newMetadata.addInjectedMethod(new WebServiceRefElement(method, pd));
              }
              else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass) &&
                  method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@EJB annotation is not supported on static methods");
                }
                if (method.getParameterTypes().length != 1) {
                  throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
                }
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
                newMetadata.addInjectedMethod(new EjbRefElement(method, pd));
              }
              else if (method.isAnnotationPresent(Resource.class) &&
                  method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@Resource annotation is not supported on static methods");
                }
                Class[] paramTypes = method.getParameterTypes();
                if (paramTypes.length != 1) {
                  throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
                }
                if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
                  PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
                  newMetadata.addInjectedMethod(new ResourceElement(method, pd));
                }
              }
            }
          });
          metadata = newMetadata;
          this.injectionMetadataCache.put(clazz, metadata);
        }
      }
    }
    return metadata;
  }    

  ...
}

Controlling start-up and detecting end of thread executions using CountDownLatch

1 comments:

MANOJ KUMAR said...

Visit http://efectivejava.blogspot.in/ and know how lazy initialization affect the performance.
How lazy can we afford to be???

Post a Comment