Managing Local Data On A Thread in Java
Managing Local Data On A Thread in Java
by Keld H. Hansen
Introduction
Recently one of my colleagues introduced me to a class from the JDK which I hadn't heard of before: ThreadLocal. This class gives you the possibility to put local data on a thread, so every module running in the thread can access it. Actually ThreadLocal has been around since JDK 1.2, but was not used much, maybe because of a first, rather poor--performance wise-implementation. This article will explain how the ThreadLocal class works, and give several examples on its use.
Object get() Returns the value for the current thread set(Object) Sets a new value for the current thread Object initialValue() Used to return an initial value (if ThreadLocal is subclassed) remove() In JDK 5 only - used to delete the current thread's value (for clean-up only) The simplest way to use a ThreadLocal object is to implement it as a singleton. Here's an example where the value stored in the ThreadLocal is a List:
public class MyThreadLocal { private static ThreadLocal tLocal = new ThreadLocal(); public static void set(List list) { tLocal.set(list); } public static List get() { return (List) tLocal.get(); } . . .
It's now a simple thing to set or get the current thread's value:
MyThreadLocal.set(list); . . . list = MyThreadLocal.get();
First time you use this technique it may seem a bit like magic, but behind the scene the local data is simply fetched using some kind of unique ID of the thread.
A servlet example
When could ThreadLocal be an appropriate technique to use? The answer is: whenever several components share the same thread, but not a common data area. To illustrate the technique we'll expand the servlet case mentioned above. When a request hits a web server, it's mapped to a servlet. Struts applications for example use the ActionServlet as the common entry point. Before the servlet is executed one or more "filters" may be executed. A filter is a Java program implementing the servlet Filter interface. The filters are defined in web.xml, for example like this:
. . . <filter> <filter-name>TestFilter</filter-name> <filter-class>hansen.playground.TestFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> . . .
The filter in this example will be run before every servlet, and there is no direct relation between the filter and a servlet. A ThreadLocal object could therefore be used to share data between the filter and the servlet. But in this case there is a more obvious solution: both the filter and the servlet share a request and a session object, and these would therefore be more natural choices. But what if the servlet calls some component - in the same thread - that does not have access to the request object or other servlet objects? Then it would make sense to pass data through a ThreadLocal object. A case that immediately springs to mind is when a logging system is used. When something is logged it's done through the parameters to the log methods. But when using a ThreadLocal object, the logging system may fetch its local thread data by a simple call like the one above:
list = MyThreadLocal.get();
These data may then be added to the log parameters. It's obvious that data like user ID, browser type etc. might be useful to include in log messages, perhaps especially in system error messages.
a name part and value part. The data in the list can be written out in a format like this: name1=value1 name2=value2 ... The string pairs are held in the class Observed, which is a simple bean:
package hansen.playground; public class Observed { private String text; private String data; public Observed(String text, String data) { this.text = text; this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
for (Iterator it = list.iterator(); it.hasNext();) { Observed obs = (Observed) it.next(); s.append(obs.getText() + "=" + obs.getData() + "\n"); } return s.toString(); } public static void listData() { System.out.println(getFormatted()); } }
public final class GetURLAction extends Action { private static final Logger logger = Logger.getLogger(GetURLAction.class.getName()); public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { long startTime = System.currentTimeMillis(); logger.info("Some info from GetURLAction"); . . . long stopTime = System.currentTimeMillis(); Observed obs = new Observed("Time used in servlet", (stopTime - startTime) + ""); MyThreadLocal.add(obs); // Forward control to the given action return (mapping.findForward("ok")); } }
A word of warning
There are a couple of things you should note when you're using ThreadLocals. Be careful to initialize the ThreadLocal object whenever the thread starts working, or you might end up connecting a logged-off user's data to another user's thread. So when you're using ThreadLocals in a situation like the one described above, where a filter initializes the object with user data, then there must be no other entries to your servlet application than through the filter. Always remember that the ThreadLocal data is connected to the thread, not the user or anything else. A good idea is therefore to delete the ThreadLocal data when it's no longer used--here's an addition to the filter class:
. . . // Go to the servlet chain.doFilter(request, response); MyThreadLocal.listData(); MyThreadLocal.set(null); . . .
Conclusion
Whenever several non-related components running in the same thread has a need for sharing data, the ThreadLocal class could be a solution. It's very simple to use this class, but you should have in mind that proper initialization and clean-up of local data is essential to avoid mixing data from different unit-of-works.
Other ressources
All the programs and files from the article are in this zip-file. An article with more information on ThreadLocal