Java.lang.Thread中的新附加字段,这是什么意思?

7
在Java 8中,java.lang.Thread类新增了3个字段:
/** 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;

正如Javadoc中所述,这些变量仅由类java.util.concurrent.ThreadLocalRandom进行管理。

此外,在ThreadLocalRandom中,它们被用于非常奇特的方式:

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

同样的代码片段也可以在LockSupport类中找到。

然后这些偏移量在几个java.concurrent的地方被内部使用。

这个想法是什么?为什么这些字段放在java.lang.Thread内部?为什么不放在ThreadLocalRandom中?


2
对于之前关于 "tlr" 的问题,我一开始并没有/现在也没有想法。 - luk2302
@luk2302 很棒的回答,非常感谢你,但是有人给我的问题投了反对票,所以我不得不删除它。对此我深感抱歉。 - Andremoniy
2
踩票并不是删除问题或答案的理由,例如我给了你一个赞,使得它的分数为0。 - luk2302
2个回答

5

这些是内部字段。只有JDK开发人员才能提供解释。我找到了一篇Doug Lea于2013年1月发布的文章,解释了这些字段背后的原理以及为什么它们在Thread类中。

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.


非常感谢您提供的论文链接,+1。但是这个解决方案看起来有点凌乱,您觉得呢? - Andremoniy
另外,我真的不明白为什么这些字段不能放在ThreadLocalRandom类中? - Andremoniy
2
@Andremoniy,老实说,我没有资格评判JDK开发人员自己制定的技术解决方案。它们被添加到Thread中的原因可能归结于Doug Lea的这条评论:“但主要是:‘永久’线程本地变量的空间需要放置在某个地方;为什么不选择 提出最少后勤问题的地方呢? 请注意,java.lang.Thread类只是系统中每个线程存储的冰山一角。 因此,添加16字节几乎是无法检测到的。” - Tunaki
2
@Andremoniy:如果这些字段在ThreadLocalRandom中声明,那么每个线程都需要一个独立的ThreadLocalRandom实例,这需要使用ThreadLocalRandom.current()来使用ThreadLocal变量,但这被认为不够高效。因此,ThreadLocalRandom.current()返回一个单例实例,这很便宜,但意味着它不能托管这些必须在每个线程中都是唯一的字段。因此,根据当前的实现,这些字段存储在Thread实例中。 - Holger

1
我会添加一个小回答,因为我刚刚在LongAdder中遇到了这个问题,而且有一个很棒的视频,Shipilev用简单的话解释了这个问题(它是用俄语讲的),这是链接: ThreadLocalRandom 对于ForkJoinPool来说,它需要将任务放入队列并从队列中删除,使用一个好的PRNG可以解决这个问题。
这个prng必须非常快且高度可扩展。嗯,Java已经有了一个:ThreadLocalRandom。为了将这些字段放入ThreadLocalRandom中,它需要一个ThreadLocal,而ThreadLocal又在内部使用ThreadLocalMap(类似HashMap)。
ThreadLocal.get(类似HashMap#get)比直接从Thread获取这些字段要慢得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接