tyrex.util
Class FastThreadLocal

java.lang.Object
  extended bytyrex.util.FastThreadLocal
All Implemented Interfaces:
java.lang.Runnable

public class FastThreadLocal
extends java.lang.Object
implements java.lang.Runnable

This is an efficient implementation of ThreadLocal which uses a background thread to remove stale thread entries, doing away with the inefficiencies of WeakHashMap. This implementation does not support ThreadLocal.initialValue().


The default ThreadLocal implementation in Sun's JDK 1.2 is implemented very efficiently, if you count lines of code. But blunt reuse of WeakHashMap doesn't lead to overly efficient use of CPU power.

One problem with ThreadLocal, which we're all aware of, is synchronization that through use of a generic adaptor applies to both lookup and insert/removal, whether justified or not. I estimate that getting rid of this will improve performance 10% - way less interesting than the optimizations to follow on.

The second problem with ThreadLocal is the use of WeakReference. Each time we look up an entry in the hash map we have to get the actual object from the weak reference and performs an equals operation on it. Each time we add a new entry we have to create a WeakReference object. If we can just get rid of that overhead, the critical loop condition will change from entry.key.get().equals( thread ) to entry.key == thread and we save one object creation.

The third problem with ThreadLocal is the obscure logic required to make it work with a generic HashMap. Because HashMap does not allow null values, each value must be encapsulated in an Entry object, another object creation and indirect access. In addition, each entry requires at least one get() and one set() methods calls.

Optimizing ThreadLocal is easier done that described. First, I have to explain how we can do without WeakReference. We assume that threads are managed outside our code (e.g. by the RMI layer) and so are created and destroyed at will. ThreadLocal has no knowledge of when they expire, and certainly does not want to hold expired objects. WeakReference is one solution.

A Thread typically has three states: prior to run(), during run() and after run(). Prior to run() the thread does not participate in ThreadLocal, so we simply ignore this state. After run() the thread is no longer needed with ThreadLocal (it's value cannot be retrieved), so we can remove it, whether or not it has been garbage collected (no need to wait for WeakReference).

We can use isActive() to find what state a thread is in. The Thread will only be active during run(), and since it won't appear in the table prior to run(), we know that an in-active thread has terminated and may be removed.

The burden of removing entries for stale threads now shifts from WeakReference and the garbage collector to a background running thread that simply removes all non isActive() threads. There is no need to rehash the table since it never changes in size, so the loop is very simple and efficient. Running at a low priority every 10 minutes is good enough for most cases.

If we constantly create and destroy threads, the daemon will have a lot of cleaning up to do. But in such a scenario, we waste so much CPU on thread creation/destruction, the daemon is the least of our worries. Once a thread is no longer alive, there is no reason to remove it immediately from the table. An in-active Thread is just an object taking a few bytes in memory, it is no longer a scarce OS resource as a live thread.

Once I took WeakReference out of the equation, the code became indirect and way more efficient. Since I only needed simple operations (get, set, remove), implementing the hashtable was a breeze. And since my implementation accepts null values, I do not need a separate Entry object, get() can perform a set() from initialValue() automatically with no need for a separate call to get(), etc.

For the heck of it, I also cut down on the use of synchronization. We know that the same hashtable entry will never be looked/inserted/removed from two threads concurrently, since it's tied to the active thread. So synchronization on lookup is no longer necessary, and synchronization on insert/remove is limited to a short sequence of code.

Version:
$Revision: 1.8 $ $Date: 2001/03/12 19:20:21 $
Author:
Assaf Arkin

Constructor Summary
FastThreadLocal()
           
FastThreadLocal(int size)
           
 
Method Summary
 java.lang.Object get()
           
 java.lang.Object get(java.lang.Thread thread)
           
 java.lang.Thread[] listThreads(java.lang.Object value)
           
 void remove(java.lang.Thread thread)
          Called to remove an entry for a given thread.
 void run()
           
 void set(java.lang.Object value)
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

FastThreadLocal

public FastThreadLocal()

FastThreadLocal

public FastThreadLocal(int size)
Method Detail

get

public java.lang.Object get()

set

public void set(java.lang.Object value)

get

public java.lang.Object get(java.lang.Thread thread)

remove

public void remove(java.lang.Thread thread)
Called to remove an entry for a given thread. Not that useful, set( null ) is a better option and the background thread will remove stale threads. This code is just here to illustrate how to perform synchronized removal on this table.


listThreads

public java.lang.Thread[] listThreads(java.lang.Object value)

run

public void run()
Specified by:
run in interface java.lang.Runnable


Original code is Copyright (c) 1999-2001, Intalio, Inc. All Rights Reserved. Contributions by MetaBoss team are Copyright (c) 2003-2005, Softaris Pty. Ltd. All Rights Reserved.