Difference between thread's context class loader and normal classloader
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
In the Java programming language, managing how classes are loaded into the JVM (Java Virtual Machine) is a fundamental aspect of runtime behavior. Class loading in Java can be done through various class loaders. Specifically, understanding the difference between a thread's context class loader and a normal class loader is crucial for developers, especially when working in environments with multiple class loaders like web servers or application containers.
Understanding ClassLoaders
A ClassLoader in Java is an object responsible for loading classes and resources from various sources like local file systems, network locations, etc., into the Java Virtual Machine. By default, Java uses a delegation model for class loaders: each instance of ClassLoader has a parent class loader, and when asked to find a class or resource, the class loader delegates the search to its parent class loader before attempting to find the class itself.
Normal ClassLoader
The "normal" class loaders in Java are typically instances of ClassLoader used within the Java application to segregate and encapsulate the loading of classes. These include:
- Bootstrap Class Loader - It loads the core Java APIs located in the
<JAVA_HOME>/jre/libdirectory and is written in native code. - Extension Class Loader - It loads the extensions of the standard core Java classes, which exist within the
<JAVA_HOME>/jre/lib/extdirectory or any other directory specified by thejava.ext.dirssystem property. - System/Application Class Loader - It loads the application specific classes from the classpath specified by the
java.class.pathsystem property, which typically includes the classpath or the jar files.
Thread's Context Class Loader
The context class loader of a thread, often simply referred to as the Thread Context Class Loader (TCCL), is an instance of ClassLoader that is used by threads to find and load classes and resources. The uniqueness of TCCL lies in the fact that it can be set (setContextClassLoader) and fetched (getContextClassLoader) dynamically at runtime by different threads of the same application.
Comparison and Differences
While the normal class loading mechanism strictly follows the delegation model from child to parent, the thread's context class loader does not inherit this property and can be set to any ClassLoader reference. This feature is particularly useful in scenarios where an application is dealing with multiple class loaders.
For example, in a J2EE environment or Spring Framework, the application server can set the context class loader of a thread to its module class loader to ensure that Enterprise JavaBeans (EJBs), servlets, and other components have access to the correct libraries and resources they depend on, which might not be visible to the system class loader.
The following table summarizes the key points of difference:
| Feature | Normal ClassLoader | Thread's Context Class Loader |
| Inheritance | Inherits from parent class loader | No inheritance, can be set independently |
| Scope | Global or per application | Per thread |
| Flexibility | Less flexible, follows strict parent-first delegation | Highly flexible, can load classes which are not in the parent path |
| Usage | Used primarily for loading classes and resources | Comfortably handles situations with multiple class loaders, specific to contexts like servlets, EJBs |
| Configuration | Typically static, defined in application startup | Dynamic, can be changed during runtime |
Practical Implications
Indeed, the decision of whether to use the normal class loaders or the context class loader of a thread depends on the specific needs of the application and the environment it operates in. Using TCCL can resolve many issues related to class loading in complex environments like Java EE. However, incorrect usage can lead to problems such as ClassNotFoundException or NoClassDefFoundError, indicating issues in visibility of the required classes across the different class loaders.
In conclusion, both class loaders serve critical roles in Java's runtime environment, but they fulfill these roles in significantly different ways, addressed to various architectural needs and issues. Identifying and applying the right type of class loader in the appropriate context is vital for building robust, scalable Java applications.

