A lightweight thread is a Thread

Andrew Haley aph at redhat.com
Wed Oct 23 17:38:28 UTC 2019


On 10/23/19 1:49 PM, David Lloyd wrote:
> On Wed, Oct 23, 2019 at 3:59 AM Andrew Haley <aph at redhat.com> wrote:
>>
>> On 10/22/19 5:27 PM, David Lloyd wrote:
>>> One would still have to allow subclassing of threads though IMO, even
>>> in the lightweight case.  This is enormously useful
>>
>> What is it about subclassing Thread that is enormously useful? I can't
>> immediately think of any case where a scoped local couldn't achieve the
>> same thing. Instead of
>>
>>    (MyFoo)Thread.current();
>>
>> you'd have
>>
>>    CurrentFoo.get();
>>
>> ... which would work in lightweight and heavyweight Threads. Is there
>> something that definitely wouldn't work with this mechanism?
> 
> Apart from being the fastest possible way to have thread local values
> in Java,

I don't think you'd notice any difference:

Benchmark                                Mode  Cnt  Score   Error  Units
ThreadLocalTest.getFieldFromLocal        avgt    3  1.728 ± 0.018  ns/op
ThreadLocalTest.getFieldFromThread       avgt    3  1.439 ± 0.003  ns/op

    @Benchmark
    public void getFieldFromLocal(MyState state) {
        Sink.getSink().drain(BenchmarkState.myScoped7.get());
    }


    @Benchmark
    public void getFieldFromThread(MyState state) {
        Sink.getSink().drain(((MyThread)Thread.currentThread()).userObjectField);
    }

That's 6 clock cycles (@ 3.5GHz) for the scoped value, 5 for the field
in Thread. Darn! :-)

In contrast, ThreadLocal:

    @Benchmark
    public void getFieldFromThreadLocal(MyState state) {
        Sink.getSink().drain(BenchmarkState.t1.get());
    }

Benchmark                                   Mode Cnt  Score   Error  Units
ThreadLocalTest.getFieldFromThreadLocal     avgt   3  4.922 ± 2.382  ns/op

(17 clocks.)

The usual caveats: C2 heuristics can completely mess this up, so YMMV.
Benchmarking such extremely narrow intervals of time is very tricky,
even with JMH. This is a Threadripper 2950X and I think its L1 cache
latency is 5 clocks.

> there are a couple things we can only do by subclassing Thread.  In
> JBoss EAP and WildFly, we support custom interrupt handlers (this is
> something that is in the JDK but is not public, thus subclassing
> Thread is the only way to do it) and interrupt deferral (only Thread
> subclasses can do this).

I don't know how much of this will be supported by lightweight
threads. I could guess but I'll leave that to the people involved.

> Vert.x/Netty uses a custom thread subclass to support "faster"
> thread locals (though looking at the code I suspect these "faster"
> thread locals might be heavier than the JDK's thread local support).

    @Benchmark
    public void getFieldFromFastThreadLocal(MyState state) {
        Sink.getSink().drain(BenchmarkState.f1.get());
    }

Benchmark                                   Mode Cnt  Score   Error  Units
ThreadLocalTest.getFieldFromFastThreadLocal avgt   3  2.287 ± 0.063  ns/op

10 clocks. That's actually pretty damned good considering they
didn't touch the JVM. There is the trade-off, though, that every
thread that uses this has a fixed-size array of thread locals at least
as large as the highest-numbered FastThreadLocal it uses.

It's a really sweet idea, but I'm not sure I could get away with it
in a general-purpose facility because of scaling issues. Food for
thought...

> Sometimes you want to be able to do `ThreadSubclass foo =
> notCurrentThread(); foo.something();`.  From what I understand, one
> can't easily get a scoped local for some other thread.

True. Apart from any other issues I don't do anything thread-safe. TBH
I'm not sure if this is safe for ThreadLocal either, unless they are
very careful about concurrent access to the pool of ThreadLocals.

> We also use it for user-supplied thread shutdown hooks, but of course
> that can also be done by wrapping the Runnable (via ThreadFactory for
> instance).
> 
> I did a search in my IDE's Quarkus code base and in the JDK and
> supporting projects, there are presently 374 Thread subclasses.  I'm
> not sure what percentage of those really *needs* to be an actual
> subclass though.  Most of the JDK cases (based on a random sampling)
> appear to just override the `run()` method and probably carry over (at
> least stylewise) from a very old JDK.

Yes, I'd guess so.

-- 
Andrew Haley  (he/him)
Java Platform Lead Engineer
Red Hat UK Ltd. <https://www.redhat.com>
https://keybase.io/andrewhaley
EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671



More information about the loom-dev mailing list