The Java SE 7 release contains an important enhancement for multithreaded custom class loaders. In previous releases, certain types of custom class loaders were prone to deadlock. The Java SE 7 release modifies the locking mechanism to avoid deadlock.
The function of a java.lang.ClassLoader
is to
locate the bytecode for a particular class, then transform that
bytecode into a usable class in the runtime system. The runtime
system provides class loaders that can locate bootstrap classes,
extension classes, and user classes. The CLASSPATH
environment variable is one way to indicate to the runtime system
where the bytecode is located.
Knowing about the CLASSPATH
environment variable is
all you may need to know about class loading. In some specific
situations, however, customizing the behavior of a class loader by
creating your own subclass may be necessary.
Custom class loaders will not run into deadlocks if they adhere
to an acyclic class loader delegation model. Acyclic delegation is
what the architects of ClassLoader
envisioned. In this
model, every class loader has a parent (delegate). When a class is
requested, the class loader first checks if the class was loaded
previously. If the class is not found, the class loader asks its
parent to locate the class. If the parent cannot find the class,
the class loader attempts to locate the class itself.
In earlier releases of the Java platform, multithreaded custom class loaders could deadlock when they did not have an acyclic delegation model. Here is one example:
Class Hierarchy: class A extends B class C extends D ClassLoader Delegation Hierarchy: Custom Classloader CL1: directly loads class A delegates to custom ClassLoader CL2 for class B Custom Classloader CL2: directly loads class C delegates to custom ClassLoader CL1 for class D Thread 1: Use CL1 to load class A (locks CL1) defineClass A triggers loadClass B (try to lock CL2) Thread 2: Use CL2 to load class C (locks CL2) defineClass C triggers loadClass D (try to lock CL1)
Synchronization in the ClassLoader
class was
previously heavy-handed, or in technical terms, not sufficiently
granular. A request to load a class synchronized on the entire
ClassLoader
object, which made it prone to
deadlock.
The Java SE 7 release includes the concept of a parallel capable class loader. Loading a class by a parallel capable class loader now synchronizes on the pair consisting of the class loader and the class name.
In the previous scenario, using the Java SE 7 release, the threads are no longer deadlocked, and all classes are loaded successfully:
Thread 1: Use CL1 to load class A (locks CL1+A) defineClass A triggers loadClass B (locks CL2+B) Thread 2: Use CL2 to load class C (locks CL2+C) defineClass C triggers loadClass D (locks CL1+D)
Custom class loaders that do not have a history of deadlocks do not require any changes. In particular, you do not need to change custom class loaders that follow the recommended acyclic hierarchical delegation model, that is, delegating first to their parent class. For backward compatibility, the Java SE 7 release continues to lock a class loader object unless it registers as parallel capable.
To create new custom class loaders, the process is similar in
the Java SE 7 release as in previous releases. Create a subclass of
ClassLoader
, then override the
findClass()
method and possibly
loadClass()
. Overriding loadClass()
makes
your life more difficult, but it is the only way to use a different
delegation model.
If you have a custom class loader with a risk of deadlocking, with the Java SE 7 release, you can avoid deadlocks by following these rules:
java.lang.ClassLoader
uses a locking scheme based on
the requested class name.java.lang.ClassLoader
's static method
registerAsParallelCapable()
. This registration
indicates that all instances of your custom class loader are
multithread safe.registerAsParallelCapable()
method in their class
initializers. Ensure that they are multithread safe for concurrent
class loading.If your custom class loader overrides only
findClass(String)
, you do not need further changes.
This is the recommended mechanism to create a custom class
loader.
If your custom class loader overrides either the protected
loadClass(String, boolean)
method or the public
loadClass(String)
method, you must also ensure that the
protected defineClass()
method is called only once for
each class loader and class name pair.
If your product ships and appears to have problems due to
incomplete handling of critical sections, you can use a new VM flag
-XX:+AlwaysLockClassLoader
. This flag reverts to
locking the class loader lock before invoking your custom class
loader's findClass()
or loadClass()
method, even for class loaders that register as parallel
capable.