Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
58 views

Managing Local Data On A Thread in Java

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.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
58 views

Managing Local Data On A Thread in Java

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.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

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.

The ThreadLocal class


The first part of the JavaDoc has this text: This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). An example may clarify this a bit. A servlet is executed in a thread, but since many users may use the same servlet at the same time, many threads will be running the same servlet code concurrently. If the servlet uses a ThreadLocal object, it can hold data local to each thread. The user ID is a good example of what could be stored in the ThreadLocal object. I like to think of this object as a hash map where some kind of thread ID is used as the key. The ThreadLocal class has these methods: Method Purpose

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.

Coding with ThreadLocals


In the following part I'll show what code is needed to implement a scenario with a filter, a servlet, a jsp error-page, and a logging system. First of all: In this example the object stored in the ThreadLocal is a list containing pairs of strings:

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; } }

To ease using the ThreadLocal object we wrap it in our own object:


package hansen.playground; . . . public class MyThreadLocal { private MyThreadLocal() { } private static ThreadLocal tLocal = new ThreadLocal(); public static void set(List list) { tLocal.set(list); } public static List get() { return (List) tLocal.get(); } public static void add(Observed obs) { List list = MyThreadLocal.get(); list.add(obs); } public static String getFormatted() { StringBuffer s = new StringBuffer(); List list = get();

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()); } }

Coding the servlet filter


In the filter we may--as an example--chose to store some browser information and the cpu time used. If you have access to data from a logon process they would be natural to store also.
package hansen.playground; . . . public final class TestFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { long startTime = System.currentTimeMillis(); MyThreadLocal.set(new ArrayList()); HttpServletRequest req = (HttpServletRequest)request; String user = (String)req.getHeader("user-agent"); Observed obs = new Observed("User agent", user); MyThreadLocal.add(obs); long stopTime = System.currentTimeMillis(); obs = new Observed("Filter start time", (stopTime - startTime) + ""); MyThreadLocal.add(obs); // Go to the servlet chain.doFilter(request, response); // Return from the servlet // type out the contents of ThreadLocal MyThreadLocal.listData(); } . . . }

Coding the servlet


In the servlet class (a Struts Action class) we could also store the cpu time, and we could call our logging system (the complete program is found in the resources section):
package hansen.playground; . . .

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")); } }

Coding the log formatter


This example uses the JDK 1.4 logging system, and if you add your own "formatter" to it, then it's utterly simple to fetch the data from the ThreadLocal object and write it out with the other logging data:
package hansen.playground; . . . public class MyFormatter extends Formatter { public String format(LogRecord record) { String myData = MyThreadLocal.getFormatted(); return ... formatted data from "record" and "myData" ... } }

Coding the error.jsp page


Finally, if you have an error page (error.jsp), you may let it print the contents of the ThreadLocal object like this:
<%@ page language="java" %> <%@ page import="hansen.playground.*" %> <h3>In error.jsp</h3> <% out.println("<pre>"+MyThreadLocal.getFormatted()+"</pre>"); %>

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

You might also like