Briefly, a thread’s “probe” value is a non-zero hash code that (probably) does not collide with other existing threads with respect to any power of two collision space. When it does collide, it is pseudo-randomly adjusted (using a Marsaglia XorShift).
简单的说,一个线程的probe探针值,是一个非零hash值,它不会和其他线程重复(一定情况下)。
但是不重复的情况指的是在int的范围内不重复( − 2 31 ∼ 2 31 − 1 -2^{31}\sim2^{31}-1 −231∼231−1),在有限的桶位面前,由于只取probe值的后几位bit,那么很大概率也会重复。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ThreadLocalRandom;
public class test4 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
ThreadLocalRandom a = ThreadLocalRandom.current();
Class<ThreadLocalRandom> clazz = ThreadLocalRandom.class;
Method getMethod = null;
Method advanceMethod = null;
try {
getMethod = clazz.getDeclaredMethod("getProbe");
advanceMethod = clazz.getDeclaredMethod("advanceProbe", int.class);
getMethod.setAccessible(true);
advanceMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
int h;
System.out.println(h = (Integer) getMethod.invoke(a));
System.out.println(getMethod.invoke(a));
System.out.println(getMethod.invoke(a));
System.out.println(h = (Integer) advanceMethod.invoke(a, h));
System.out.println(getMethod.invoke(a));
System.out.println(getMethod.invoke(a));
System.out.println(h = (Integer) advanceMethod.invoke(a, h));
System.out.println(getMethod.invoke(a));
System.out.println(getMethod.invoke(a));
}
}
-1640531527
-1640531527
-1640531527
1359758873
1359758873
1359758873
-533834434
-533834434
-533834434
由于两个方法不是public,需要通过反射才能调用。
getProbe
获得当前的探针值,如果从来没有调用过advanceProbe
,那么获得的是,probe序列的第一个值(即probe种子)。advanceProbe
根据旧probe值生成新的probe值。import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ThreadLocalRandom;
public class test3 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
Class<ThreadLocalRandom> clazz = ThreadLocalRandom.class;
Method getMethod = null;
Method advanceMethod = null;
try {
getMethod = clazz.getDeclaredMethod("getProbe");
advanceMethod = clazz.getDeclaredMethod("advanceProbe", int.class);
getMethod.setAccessible(true);
advanceMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
for(int i = 0; i < 5; i++) {
Method finalGetMethod = getMethod;
Method finalAdvanceMethod = advanceMethod;
new Thread(()->{
ThreadLocalRandom a = ThreadLocalRandom.current();
int h;
try{
System.out.println(Thread.currentThread().getName()+" "+(h = (Integer) finalGetMethod.invoke(a)));
System.out.println(Thread.currentThread().getName()+" "+(h = (Integer) finalAdvanceMethod.invoke(a, h)));
System.out.println(Thread.currentThread().getName()+" "+(h = (Integer) finalAdvanceMethod.invoke(a, h)));
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
Thread-4 1013904242
Thread-0 -1640531527
Thread-1 2027808484
Thread-1 1143052388
Thread-1 -1836491342
Thread-3 -626627285
Thread-2 387276957
Thread-3 -144452630
Thread-0 1359758873
Thread-4 -1575449550
Thread-4 -1068750243
Thread-0 -533834434
Thread-3 784628165
Thread-2 -606781730
Thread-2 -36250631
发现多线程下使用,每个线程产生的probe序列,都没有重复的。
创建threadNum个线程,每个线程获得probeTimes次探针值,将每个线程获得过的探针值都放入同一个集合中,最后判断集合大小是否和threadNum * probeTimes
相等。
import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
public class test3 {
public static void main(String[] args) throws InterruptedException {
Class<ThreadLocalRandom> clazz = ThreadLocalRandom.class;
Method getMethod = null;
Method advanceMethod = null;
try {
getMethod = clazz.getDeclaredMethod("getProbe");
advanceMethod = clazz.getDeclaredMethod("advanceProbe", int.class);
getMethod.setAccessible(true);
advanceMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
int threadNum = 5;
int probeTimes = 100000;
Thread[] ThreadArray = new Thread[threadNum];
Set<Integer> s = ConcurrentHashMap.<Integer> newKeySet();
for(int i = 0; i < threadNum; i++) {
Method finalGetMethod = getMethod;
Method finalAdvanceMethod = advanceMethod;
ThreadArray[i] = new Thread(()->{
ThreadLocalRandom a = ThreadLocalRandom.current();
int h = 0;
try{
for(int j = 0;j <probeTimes; j++) {
if (j == 0) {//第一次去get
h = (Integer) finalGetMethod.invoke(a);
} else {//之后每次都增加
h = (Integer) finalAdvanceMethod.invoke(a, h);
}
s.add(h);
}
} catch (Exception e) {
e.printStackTrace();
}
});
ThreadArray[i].start();
}
for(int i = 0; i < threadNum; i++) {
ThreadArray[i].join();
}
System.out.println("探针生成的总次数:"+threadNum * probeTimes);
System.out.println("不重复的探针数:"+s.size());
}
}
int threadNum = 10000;
int probeTimes = 82;
ThreadLocalRandom
的probe机制在线程数比较少,且probe移动次数也比较小时,是可以保证探针不重复的。 /** The common ThreadLocalRandom */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
不管有多少个线程来调用ThreadLocalRandom.current()
,返回的都是同一个实例,因为它是一个单例。
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
但即使这样,也应该让每个线程分别调用ThreadLocalRandom.current()
,为的是让每个线程分别调用localInit
。因为localInit
里会对当前线程Thread初始化设置threadLocalRandomSeed
值和threadLocalRandomProbe
值。
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);//初始化设置
UNSAFE.putInt(t, PROBE, probe);//初始化设置
}
private static final long SEED;
private static final long PROBE;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
} catch (Exception e) {
throw new Error(e);
}
}
//Thread.java
/** 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;
private static final AtomicInteger probeGenerator =
new AtomicInteger();
private static final int PROBE_INCREMENT = 0x9e3779b9;
每个线程通过int p = probeGenerator.addAndGet(PROBE_INCREMENT);
获得probe序列的种子,每个线程肯定获得的都不一样,因为每个线程都累加了PROBE_INCREMENT
。
没有调用过ThreadLocalRandom.current()
,ThreadLocalRandom.getProbe()
获得的肯定是0(对象默认的初始化)。