[concurrency-interest] ThreadLocal.getIfPresent()

David Lloyd david.lloyd at redhat.com
Thu Jun 7 13:47:11 EDT 2018

On Thu, Jun 7, 2018 at 3:02 AM, Peter Levart via Concurrency-interest
<concurrency-interest at cs.oswego.edu> wrote:
> The problem with getIfPresent() returning null for ThreadLocal<Optional<X>>
> is it violates the convention that methods returning Optional<X> must never
> return null. So Optional<X> is not a good choice for the T in
> ThreadLocal<T>.

+1 inasmuch as Optional is good for anything, it is definitely not
good for this.

> I also like Martin's ideas of introducing compute* methods similar to what
> Map offers.
> Those methods are most welcome in concurrent scenarios where they provide
> atomicity. In single-threaded scenarios they provide a kind of unification
> of Map vs. ConcurrentMap API which is good to be able to create common logic
> that works in both settings. ThreadLocal is not a Map and is never going to
> be a Map (as a subtype) and is always used in single-threaded scenarios, so
> compute* -like methods in ThreadLocal would just be an attempt to try to
> optimize a read-compute-write scenario so that it would involve a single
> hash-lookup operation. As compute- -like methods need lambdas and lambdas
> typically have to capture state, the optimization attempt might not be that
> effective.
> But I agree that compute* (or maybe just compute(BinaryOperator)) is a
> viable alternative to getIfPresent() and/or isPresent() from purely
> functional standpoint. What I (and others) are trying to achieve is a way to
> "peek" into the state of ThreadLocal without actually triggering the
> initialization which might be heavy.
> Adding compute to ThreadLocal would also provide another (the third)
> alternative way of associating a value with current thread:
> 1 - using get() in combination with initialValue()
> 2 - using set()
> 3 - using compute()
> Is this good or does it complicate the API too much?

The get() method can be overridden; what does this mean for the
semantics of compute()?  And what does compute() actually do with an
initial value?  Is the initial value constructed and supplied to the
compute() lambda or do we supply null in this case?

I think that any answer to any of these questions is going to be
unintuitive and/or annoying for _some_ use case or another.  Sticking
to isPresent(), get(), and set() keeps things simple and obvious, and
is probably save for the reasons you outlined above.  The double hash
lookup sucks, conceptually, but realistically won't all the relevant
info be very likely to be in L1 cache for the second lookup in most
cases?  I doubt it's a real problem other than being "itchy".


More information about the Concurrency-interest mailing list