ThreadLocal类和ThreadLocalRandom类
一、ThreadLocal类
ThreadLocal可以视作为一个工具类,它并不包含线程变量,线程变量都存储在各自Thread实例中ThreadLocals变量中,实现了线程变量内存隔离,保证线程安全(以空间换取的)。Thread类中变量
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocal类核心方法,set与get:实际控制Thread.currentThread().threadLocals对象来操作当前线程的变量
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals;//取当前线程Thread对象中的ThreadLocals变量 }
实现:
/** * 优点:线程之间变量内存隔离,线程安全 * 缺点:变量保存到当前线程中,线程不结束会一直占用内存,可能导致内存泄漏,需要调用remove清除;ThreadLocal保存到当前线程threadLocals中,不具有继承性,父线程的threadLocalMap取不到; */ public class ThreadLocalTest { static ThreadLocal<String> localVariable = new ThreadLocal<>(); static void print(String str){ System.out.println(str + ":" + localVariable.get()); localVariable.remove();//内存泄漏 } public static void main(String[] args){ Thread threadOne = new Thread(new Runnable() { @Override public void run() { localVariable.set("threadOne local variable"); localVariable.set("threadOne local variable2"); print("threadOne"); System.out.println("threadOne remove after" + ":" + localVariable.get()); } }); Thread threadTwo = new Thread(new Runnable() { @Override public void run() { localVariable.set("threadTwo local variable"); print("threadTwo"); System.out.println("threadTwo remove after" + ":" + localVariable.get()); } }); threadOne.start(); threadTwo.start(); } }
二、InheritableThreadLocal类
继承ThreadLocal类,主要解决第二个缺点ThreadLocal不具有继承性,父线程变量取不到
因此在Thread类中加入了inheritableThreadLocals用来存储父线程的变量的拷贝
注意:父线程变量拷贝是在Thread.init方法即创建线程时实现的,所以需要先赋值后创建子线程
/* * Thread.init方法中 */if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
实现:
public class InheritableThreadLocalTest { static ThreadLocal<String> inheritableLocal = new InheritableThreadLocal<>(); static void print(String str){ System.out.println(str + ": parents variable :" + inheritableLocal.get()); } public static void main(String[] args) throws InterruptedException { inheritableLocal.set("mainThread variable");//父线程先赋值 Thread threadOne = new Thread(new Runnable() { @Override public void run() { print("threadOne"); } }); inheritableLocal.remove();//变量拷贝所以threadOne中不会清除 Thread threadTwo = new Thread(new Runnable() { @Override public void run() { print("threadTwo"); } }); threadOne.start(); threadTwo.start(); } }
三、Random类
public class RandomTest { public static void main(String[] args){ Random random = new Random(); for (int i = 0; i < 10; i++){ System.out.println(random.nextInt()); } } } /* * nextInt() 源码 */ protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed));//获取nextseed采用CAS保证原子性,多线程会使大量线程进行自旋重试,降低了并发性能。 return (int)(nextseed >>> (48 - bits)); }
四、ThreadLocalRandom类
继承了Random重写了nextInt,类似于ThreadLocal的实现,在各自Thread实例中保存自己的seed,具体声明源码如下。
// The following three initially uninitialized fields are exclusively // managed by class java.util.concurrent.ThreadLocalRandom. These // fields are used to build the high-performance PRNGs in the // concurrent code, and we can not risk accidental false sharing. // Hence, the fields are isolated with @Contended. /** 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;
ThreadLocalRandom实现
public class ThreadLocalRandomTest { public static void main(String[] args){ ThreadLocalRandom random = ThreadLocalRandom.current(); for (int i = 0;i < 10; i++){ System.out.println(random.nextInt(5)); } } } /** * nextseed获取源自各自线程中保存的seed,实现了线程隔离 */ final long nextSeed() { Thread t; long r; // read and update per-thread seed UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA);//Thread.currentThread.threadLocalRandomSeed return r; }
ThreadLocalRandom主要源码模块
1.unsafe初始化
// Unsafe mechanics private static final sun.misc.Unsafe UNSAFE; private static final long SEED; private static final long PROBE; private static final long SECONDARY; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> tk = Thread.class; SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed")); PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe")); SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed")); } catch (Exception e) { throw new Error(e); } }
2.构造方法私有化,提供静态方法current实例化,保证单例
/** Constructor used only for static singleton */ private ThreadLocalRandom() { initialized = true; // false during super() call } static final ThreadLocalRandom = new ThreadLocalRandom(); /** * Returns the current thread's {@code ThreadLocalRandom}.*/ public static ThreadLocalRandom current() { if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)//根据当前线程threadLocalRandomProbe标志,判断当前线程threadLocalRandomSeed是否已初始化 localInit(); return instance; } /** * Initialize Thread fields for the current thread. Called only * when Thread.threadLocalRandomProbe is zero, indicating that a * thread local seed value needs to be generated. Note that even * though the initialization is purely thread-local, we need to * rely on (static) atomic generators to initialize the values. */ static final void localInit() { int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; //初始化probe的值,p等于0时表示未初始化,所以skip掉 long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));//初始化seed的值 Thread t = Thread.currentThread(); UNSAFE.putLong(t, SEED, seed);//存到当前线程的threadLocalRandomSeed中 UNSAFE.putInt(t, PROBE, probe);// }
3.随机数
/** * Returns a pseudorandom {@code int} value between zero (inclusive) * and the specified bound (exclusive). * * @param bound the upper bound (exclusive). Must be positive. * @return a pseudorandom {@code int} value between zero * (inclusive) and the bound (exclusive) * @throws IllegalArgumentException if {@code bound} is not positive */ public int nextInt(int bound) { if (bound <= 0) throw new IllegalArgumentException(BadBound); int r = mix32(nextSeed());//重点下一个seed int m = bound - 1; if ((bound & m) == 0) // power of two r &= m; else { // reject over-represented candidates for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1) ; } return r; } final long nextSeed() { Thread t; long r; // read and update per-thread seed UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA);//取得是当前线程中的threadLocalRandomSeed变量 return r; }
注意:初始化的seed值和probe值是写死的,nextSeed方法中累加的GAMMA也是写死的,所以多个线程第n次取随机数一样
参考《Java并发编程之美》