Class LastResultOwnerOrThreadLocal<T>
- Type Parameters:
T- the type of the stored value (often a decode result such asSequence)
tag() then probs()).
Why not always ThreadLocal? Short-lived instances that are only ever used from one thread
would pay for a ThreadLocal map entry on every set(Object). This type keeps the first
thread's value in plain fields until a second thread touches the same instance; then it switches non-owner
threads to ThreadLocal storage. Once the instance has gone multi-threaded, it stays that way for
the rest of its lifetime (one-way transition).
Thread identity is tracked by thread id (a long), not by
Thread reference. Holding a strong reference to a worker thread in a long-lived component
pins the thread's context classloader in container environments (e.g. Jakarta EE) — exactly the leak this
class is designed to avoid. Thread ids can be recycled after a thread terminates, so the worst-case
outcome is that a recycled-id thread sees a stale ownerValue from a previous thread instead of
null; that is no worse than the documented contract for get() (callers are expected to
call set(Object) before get() on every thread).
Call clearForCurrentThread() when releasing pooled threads or disposing the enclosing component
to avoid classloader retention.
Relationship to other thread-safety patterns in this PR
OpenNLP's ME classes use three different strategies depending on what they need to share between
decode() and probs() on the same thread:
LastResultOwnerOrThreadLocal/OwnerOrPerThreadState— used where the public API exposes per-thread last-result state (POSTaggerME,SentenceDetectorME,TokenizerME). The owner-fast-path keeps single-threaded callers cheap.volatilefield plus method-local processing plus an atomic publish at the end — used wheredecode()returns the result directly and there is no separateprobs()-style accessor (ChunkerME,LemmatizerME,NameFinderME).- Plain
ThreadLocal— used inside the cache layers (BeamSearch,CachedFeatureGenerator,ConfigurablePOSContextGenerator) where the per-call cost of aThreadLocal.get()is dwarfed by the work it guards (e.g.MaxentModel.eval).
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidClearsThreadLocalstate for the current thread and, when this thread is the single-thread owner, clears owner fields so another thread can become owner.get()Returns the last stored value for the calling thread, ornullif none.voidRecordsvalueas the last result for the calling thread.
-
Constructor Details
-
LastResultOwnerOrThreadLocal
public LastResultOwnerOrThreadLocal()
-
-
Method Details
-
set
Recordsvalueas the last result for the calling thread.- Parameters:
value- the value to store (typically non-null after a successful decode)
-
get
Returns the last stored value for the calling thread, ornullif none.- Returns:
- last value for this thread, or
null
-
clearForCurrentThread
public void clearForCurrentThread()ClearsThreadLocalstate for the current thread and, when this thread is the single-thread owner, clears owner fields so another thread can become owner.
-