Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New additional fields in java.lang.Thread, what is the idea?

In Java 8, java.lang.Thread class got 3 new fields:

/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;

/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;

/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;

as it said in Javadoc for being exclusively managed by class java.util.concurrent.ThreadLocalRandom.

Furthermore, in ThreadLocalRandom they are used in very freakish way:

SEED = UNSAFE.objectFieldOffset
    (tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
    (tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
    (tk.getDeclaredField("threadLocalRandomSecondarySeed"));

(the same code piece can be met also in LockSupport class).

and then this offsets are used internally in several java.concurrent places.

What is the idea? Why these fields are places inside java.lang.Thread? Why not inside ThreadLocalRandom?

like image 256
Andremoniy Avatar asked Dec 30 '15 12:12

Andremoniy


2 Answers

These are internal fields. Explanations can only come from JDK developers themselves. I was able to find a post about this from Doug Lea dated January 2013, that explains the rationale behind those fields and why they are inside the Thread class.

When we introduced ThreadLocalRandom, we conservatively implemented it to use an actual ThreadLocal. However, as it becomes more widely used, it is worth improving the implementation by housing ThreadLocalRandom state (and related bookkeeping) in class Thread itself. This would entail three fields (16 total bytes).

So I propose adding the following to class Thread:

// The following three initially uninitialized fields are exclusively
// managed by class java.util.concurrent.ThreadLocalRandom.
/** The current seed for a ThreadLocalRandom */
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
int threadLocalRandomSecondarySeed;

The reasons for doing it in this way are:

  1. Uniformly faster access to ThreadLocalRandom state. While ThreadLocal access is normally pretty fast already, this is not only faster, it does not degrade in cases where user programs create large numbers of ThreadLocals, which may (probabilistically) cause any given access to become slower.

  2. Smaller total footprint for any program using ThreadLocalRandom. Three fields require less space than boxing into a padded ThreadLocal object. As ThreadLocalRandom becomes widely used within JDK itself, this includes just about all programs.

  3. Further time/space savings for java.util.concurrent ForkJoinPool, ConcurrentHashMap, LongAdder, ConcurrentSkipList, and other classes that could use this form of the unified ThreadLocalRandom bookkeeping rather than their own special-purpose ThreadLocals as they now do.

like image 190
Tunaki Avatar answered Oct 07 '22 09:10

Tunaki


I will resurrect this by adding a small answer too, since I've just hit this in LongAdder and there's a great video where Shipilev explains this in simple words (it is in russian), here is the link: ThreadLocalRandom

For the case of ForkJoinPool, it needs to put tasks into a queue and remove from a queue, which queue exaclty is solved via a good PRNG.

This prng has to be very fast and highly scalable. Well, java has one in place: ThreadLocalRandom. For these fields to be put into ThreadLocalRandom, it needed a ThreadLocal, which in turn internally uses a ThreadLocalMap(think HashMap).

ThreadLocal.get (think HashMap#get) is much slower then getting these fields directly from Thread.

like image 33
Eugene Avatar answered Oct 07 '22 08:10

Eugene