|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object tyrex.util.FastThreadLocal
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.
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 |
public FastThreadLocal()
public FastThreadLocal(int size)
Method Detail |
public java.lang.Object get()
public void set(java.lang.Object value)
public java.lang.Object get(java.lang.Thread thread)
public void remove(java.lang.Thread thread)
public java.lang.Thread[] listThreads(java.lang.Object value)
public void run()
run
in interface java.lang.Runnable
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |