Date: 2017-01-14 10:10:12
前面两篇文章《HashMap源码分析》,《Java基础-强,弱,软引用》已经把WeakHashMap用到的技术都讲完了。这篇文章也就没啥好讲的了。
WeakHashMap就是一个HashMap,只不过它的key继承了WeakReference表示key是一个弱引用,在GC时就会被回收。就这么简单。
实现步骤:
//Entry里面没看到K的定义哦,跑哪里去了?
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
int hash;
Entry next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue
解说:可以看到key直接被定义为弱引用了。
定义为弱引用之后,GC就可以随意回收它了。呵呵
expungeStaleEntries监控queue队列,当key被回收时,queue里面会接受到GC发送过来的回收消息,expungeStaleEntries这个监控到queue不为空时,就把对应的value也置为空,这样value也就可以被回收了。
WeakHashMap和HashMap基本一样,所以不细看其他部分的源码,主要是这个expungeStaleEntries方法里面。
//WeakHashMap里面定义了一个成员变量queue,GC在回收key之后会发送通知,key被回收了。发送的通知会放到queue。
private final ReferenceQueue
在《java基础-强,软,弱引用》中我们讲了弱引用的相关信息,但是别和这里的混淆哦,所以这里在讲解一下。
private static WeakHashMap
vm参数:-verbose:gc -Xms20M -Xms20M -Xmn10M -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintGCDetails -XX:SurvivorRatio=8
堆内存大小20M,年轻带堆内存10M。
在未执行到 byte[] a = new byte[1024 * 1024 * 2]这里的时候,堆内存还有足够空间,没有做回收操作,
当执行了 byte[] a = new byte[1024 * 1024 * 2]后
开始触发GC垃圾回收了,由于内存严重不够,所以连同触发了FULL GC。
回收后,对内存里面还剩下eden space 8192K, 27% used
什么问题,因为a引用的对象没有被回收,a是一个强引用。
图中为什我要在a前后都获取一次呢?为了测试value有没有被回收掉。
在WeakHashMap中,不管是get,还是put,还是size,几乎所有操作都会先去调用一下expungeStaleEntries这个方法,先把被GC回收掉的key从WeakHashMap中移除,同时把key所对应的value置为null帮助GC回收。
在图中,先添加了8M的对象在caches,并没有出发GC回收,当执行a对象时才触发GC操作,GC也只是回收key,而并不会主动回收value,那么问题来了,我并没有调用get,put,size操作啊,按道理是不会执行WeakHashMap里面的expungeStaleEntries这个方法的,因为只有操作了put,get等操作时才会进入expungeStaleEntries这个方法。但是打印出来的heap(堆)信息确告诉我,value也被回收了,那只能说明GC去调用了exp这个方法了。
上面图中,我故意使用两组做测试,第一组在GC后不做任何操作,第二组GC后,调用一下WeakHashMap个get操作,就是让他主动出发一下expungeStaleEntries,这样做就会让GC把value也回收了,但是两组对比后发现堆里面的大小是一样的,从而证明了GC回收key之后会调用一次exp这个方法。
为什么我要做这个对比呢?因为ThreadLocal这个类,它的操作和WeakHashMap是很相像的,但是ThreadLocal需要主动去清理value的。
key是弱引用,GC会帮我回收它,但是value确一直存在着,这就会导致内存泄漏了。
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。
查看ThreadLocal源码:在Thread类中保有ThreadLocal.ThreadLocalMap的引用,即在一个Java线程栈中指向了堆内存中的一个ThreadLocal.ThreadLocalMap的对象,此对象中保存了若干个Entry,每个Entry的key(ThreadLocal实例)是弱引用,value是强引用(这点类似于WeakHashMap)。用到弱引用的只是key,每个key都弱引用指向threadLocal,当把threadLocal实例置为null以后,没有任何强引用指向threadLocal实例,所以threadLocal将会被gc回收,但是value却不能被回收,因为其还存在于ThreadLocal.ThreadLocalMap的对象的Entry之中。只有当前Thread结束之后,所有与当前线程有关的资源才会被GC回收。所以,如果在线程池中使用ThreadLocal,由于线程会复用,而又没有显示的调用remove的话的确是会有可能发生内存泄露的问题。
其实在ThreadLocal.ThreadLocalMap的get或者set方法中会探测其中的key是否被回收(调用expungeStaleEntry方法),然后将其value设置为null,这个功能几乎和WeakHashMap中的expungeStaleEntries()方法一样。因此value在key被gc后可能还会存活一段时间,但最终也会被回收,但是若不再调用get或者set方法时,那么这个value就在线程存活期间无法被释放。
来看两组数据
设置堆栈大小:-verbose:gc -Xmx20M -Xms20M -Xmn10M -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintGCDetails -XX:SurvivorRatio=8
public class ThreadLockDome {
private static class ThreadMap implements Runnable {
private WeakHashMapbyte[]> a = new WeakHashMapbyte[]>();
public void run() {
try {
a.put(Thread.currentThread().getName(),new byte[1024 * 1024 * 3]);
TimeUnit.SECONDS.sleep(3);
if(a.get(Thread.currentThread().getName())!=null){
System.out.println("a==11111");
}else{
System.out.println("a=====null null");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
ThreadMap map = new ThreadMap();
Thread t = new Thread(map);
t.start();
}
Thread.sleep(5000);
byte[] c = new byte[1024 * 1024 * 2];
System.out.println("---c---");
}
}
输出日志:
[GC [PSYoungGen: 7799K->432K(9216K)] 7799K->6576K(19456K), 0.0071480 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]
[Full GC [PSYoungGen: 432K->0K(9216K)] [ParOldGen: 6144K->6446K(10240K)] 6576K->6446K(19456K) [PSPermGen: 2848K->2847K(21504K)], 0.0135840 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 6774K->3073K(9216K)] [ParOldGen: 6446K->9508K(10240K)] 13220K->12581K(19456K) [PSPermGen: 2855K->2855K(21504K)], 0.0080190 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
a==11111
a=====null null
a==11111
a==11111
a=====null null
[Full GC [PSYoungGen: 6554K->0K(9216K)] [ParOldGen: 9508K->303K(10240K)] 16062K->303K(19456K) [PSPermGen: 3039K->3039K(21504K)], 0.0098200 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
---a---
Heap
PSYoungGen total 9216K, used 2148K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
eden space 8192K, 26% used [0x00000007ff600000,0x00000007ff8192c8,0x00000007ffe00000)
from space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
to space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
ParOldGen total 10240K, used 303K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
object space 10240K, 2% used [0x00000007fec00000,0x00000007fec4bf28,0x00000007ff600000)
PSPermGen total 21504K, used 3046K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
object space 21504K, 14% used [0x00000007f9a00000,0x00000007f9cf9b58,0x00000007faf00000)
可以看到我开启了5个线程,每个线程里面存储了3M的数据。
a.put(Thread.currentThread().getName(),new byte[1024 * 1024 * 3]);
注意这里TimeUnit.SECONDS.sleep(3);
我让每个线程停留3秒种,如果不停留线程执行太快,线程执行完毕后就会立即释放内存,也就可能看不出3*5=15M内存的情况了。
当5个线程都执行到a.put的时候,它们接下来都需要等待3秒钟,也就是说占用的内存不会立即释放了,它们现在占用的内存为3*5=15M哦。
GC看到a是一个弱引用,发生GC时就会直接回收它,所以我们看到本来应该是5个a====1111,现在的结果是有两个a==null null。
说明被回收了。
接下来c又存入了2M的对象在堆里面,也没问题了,前面有部分堆内存被回收了,现在有足够的内存来容纳c了。
上面的日志不一定哦,因为是多线程,也有可能WeakHashMap里面还没回收呢就执行到了
byte[] c = new byte[1024 * 1024 * 2];就会导致c对象内存溢出,也就是主线程内存溢出。
把a里面put入4M的对象。
a.put(Thread.currentThread().getName(),new byte[1024 * 1024 * 4]);
结果:
[GC-- [PSYoungGen: 5915K->5915K(9216K)] 14107K->14107K(19456K), 0.0017590 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 5915K->4404K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 14107K->12596K(19456K) [PSPermGen: 2956K->2955K(21504K)], 0.0136470 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
[GC-- [PSYoungGen: 4404K->4404K(9216K)] 12596K->12596K(19456K), 0.0015570 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 4404K->4404K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 12596K->12596K(19456K) [PSPermGen: 2955K->2955K(21504K)], 0.0070020 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GCException in thread "Thread-3" -- [PSYoungGen: 4652K->4652K(9216K)] 12844K->12844K(19456K), 0.0019940 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 4652K->4404K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 12844K->12596K(19456K) [PSPermGen: 2955K->2955K(21504K)], 0.0089340 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC-- [PSYoungGen: 4404K->4404K(9216K)] 12596K->12596K(19456K), 0.0018320 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 4404K->4404K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 12596K->12596K(19456K) [PSPermGen: 2955K->2955K(21504K)], 0.0090810 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Exception in thread "Thread-4" java.lang.OutOfMemoryError: Java heap space
at cn.collection.ThreadLockDome$ThreadMap.run(ThreadLockDome.java:33)
at java.lang.Thread.run(Thread.java:745)
java.lang.OutOfMemoryError: Java heap space
at cn.collection.ThreadLockDome$ThreadMap.run(ThreadLockDome.java:33)
at java.lang.Thread.run(Thread.java:745)
a=====null null
a=====null null
a=====null null
---c---
Heap
PSYoungGen total 9216K, used 7162K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
eden space 8192K, 87% used [0x00000007ff600000,0x00000007ffcfe940,0x00000007ffe00000)
from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
to space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
ParOldGen total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400020,0x00000007ff600000)
PSPermGen total 21504K, used 3071K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
object space 21504K, 14% used [0x00000007f9a00000,0x00000007f9cffd80,0x00000007faf00000)
三个a被回收了,另外两个a在回收时直接抛出了堆栈溢出了。
public class ThreadLockDome {
private static class ThreadMap implements Runnable {
private ThreadLocal<byte[]> b = new ThreadLocal<byte[]>();
public void run() {
try {
b.set(new byte[1024 * 1024 * 3]);
TimeUnit.SECONDS.sleep(3);
if (b.get() != null) {
System.out.println(Thread.currentThread().getName()+"=====111111");
} else {
System.out.println(Thread.currentThread().getName()+"====null null null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadMap map = new ThreadMap();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(map);
t.start();
}
Thread.sleep(5000);
byte[] c = new byte[1024 * 1024 * 2];
System.out.println("---c---");
}
}
输出结果:
[GC [PSYoungGen: 7635K->416K(9216K)] 7635K->6560K(19456K), 0.0040800 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
[Full GC [PSYoungGen: 416K->0K(9216K)] [ParOldGen: 6144K->6445K(10240K)] 6560K->6445K(19456K) [PSPermGen: 2849K->2848K(21504K)], 0.0132550 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 6624K->3073K(9216K)] [ParOldGen: 6445K->9507K(10240K)] 13069K->12580K(19456K) [PSPermGen: 2855K->2855K(21504K)], 0.0067770 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Thread-2=====111111
Thread-4=====111111
Thread-3=====111111
Thread-1=====111111
Thread-0=====111111
[Full GC [PSYoungGen: 6612K->0K(9216K)] [ParOldGen: 9507K->303K(10240K)] 16120K->303K(19456K) [PSPermGen: 3040K->3040K(21504K)], 0.0070590 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
---c---
Heap
PSYoungGen total 9216K, used 2162K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
eden space 8192K, 26% used [0x00000007ff600000,0x00000007ff81c9f8,0x00000007ffe00000)
from space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
to space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
ParOldGen total 10240K, used 303K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
object space 10240K, 2% used [0x00000007fec00000,0x00000007fec4bfa0,0x00000007ff600000)
PSPermGen total 21504K, used 3047K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
object space 21504K, 14% used [0x00000007f9a00000,0x00000007f9cf9c38,0x00000007faf00000)
从日志可以看到虽然发生了GC和Full GC,但是ThreadLocal里面并没有被回收了,5个线程的值都不为null。
这句:[Full GC [PSYoungGen: 6612K->0K(9216K)]说明什么,当线程执行完毕后,内存就立马释放了。
改为4M后:
[GC-- [PSYoungGen: 5423K->5423K(9216K)] 13615K->13615K(19456K), 0.0039000 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 5423K->4395K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 13615K->12587K(19456K) [PSPermGen: 2811K->2810K(21504K)], 0.0163100 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
[GC-- [PSYoungGen: 4395K->4395K(9216K)] 12587K->12587K(19456K), 0.0018300 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GCException in thread "Thread-3" [PSYoungGen: 4395K->4395K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 12587K->12587K(19456K) [PSPermGen: 2810K->2810K(21504K)], 0.0077320 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC-- [PSYoungGen: 5056K->5056K(9216K)] 13248K->13248K(19456K), 0.0020430 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[Full GC [PSYoungGen: 5056K->4387K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 13248K->12579K(19456K) [PSPermGen: 2817K->2817K(21504K)], 0.0125060 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC-- [PSYoungGen: 4387K->4387K(9216K)] 12579K->12579K(19456K), 0.0014050 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 4387K->4387K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 12579K->12579K(19456K) [PSPermGen: 2817K->2817K(21504K)], 0.0182230 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
Exception in thread "Thread-4" java.lang.OutOfMemoryError: Java heap space
at cn.collection.ThreadLockDome$ThreadMap.run(ThreadLockDome.java:24)
at java.lang.Thread.run(Thread.java:745)
java.lang.OutOfMemoryError: Java heap space
at cn.collection.ThreadLockDome$ThreadMap.run(ThreadLockDome.java:24)
at java.lang.Thread.run(Thread.java:745)
Thread-2=====111111
Thread-0=====111111
Thread-1=====111111
---c---
Heap
PSYoungGen total 9216K, used 7198K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
eden space 8192K, 87% used [0x00000007ff600000,0x00000007ffd07928,0x00000007ffe00000)
from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
to space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
ParOldGen total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400020,0x00000007ff600000)
PSPermGen total 21504K, used 3071K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
object space 21504K, 14% used [0x00000007f9a00000,0x00000007f9cffe78,0x00000007faf00000)
可以看到前3个线程
Thread-2=====111111
Thread-0=====111111
Thread-1=====111111
没有被GC回收,导致后了后2个进来后就直接抛堆空间异常了。
这行日志“—c—”之所以能打印出来,就是因为5个线程执行结束后内存直接回收了,所以这样c在创建2M大小的对象时也是没有问题的。
可以看到WeakHashMap的key是弱引用,ThreadLocal的key也是用的弱引用,但是WeakHashMap在被GC回收时value也会被回收了,而ThreadLocal则不会,ThreadLocal必须显示的调用一下remove方法才能将value的值给清空。
在两个的源码中可以看到,ThreadLocal里面并没有一个接受对象被GC回收后通知的ReferenceQueue,所以就算key被回收了,value也是存在的,并不会和WeakHashMap一样,在key被清空后,可以使用ReferenceQueue这个队列接受被GC的通知,然后把value也自己给清空。
讲完了上面的再来讲源码终于明白了ThreadLocal的原理和他是用来干嘛的了,之前一直半懂半不懂的。
public void set(T value) {
//看到了吗?t是个当前线程,不懂Thread.currentThread这个的可以去看看,不然真不能理解ThreadLocal
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//获取当前线程的ThreadLocalMap,要知道“每个线程“里面都有一个ThreadLocalMap哦!
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//如果ThreadLocalMap这个为null则创建一个
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//如果ThreadLocalMap这个已经存在了,则往里面添加值ThreadLocalMap就像一个WeakHashMap
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//可以看到key为弱引用哦,可以参看WeakHashMap观看哦!
static class Entry extends WeakReference {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
TheadLocal就像一个SetHashMap,
ThreadLocalMap就像一个WeakHashMap。
SetHashMap是利用HashMap的key来做去重的,HashMap的key虽然可以重复,但是后进来的会覆盖先进来的,
put(1,”2”);
put(1,”3”);
最后存入的是put(1,”3”);
put(1,”2”)会被覆盖,
这样SetHashMap就利用key来做去重。set(1),set(1)最后遍历的时候只会有一个set(1).
ThreadLocal存数据也是set(1).他和setHashMap正好反着来。使用value做value,那么key呢?key使用的是this,表示当前线程的ThreadLocal。
接着最上面的实例分析:
private static class ThreadMap implements Runnable {
private WeakHashMapbyte[]> a = new WeakHashMapbyte[]>();
private ThreadLocal<byte[]> b = new ThreadLocal<byte[]>();
public void run() {
try {
b.set(new byte[1024 * 1024 * 1]);
a.put("1",new byte[1024 * 1024 * 1]);
TimeUnit.SECONDS.sleep(3);
if (b.get() != null) {
System.out.println(Thread.currentThread().getName()+"=====111111");
} else {
System.out.println(Thread.currentThread().getName()+"====null null null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadMap map = new ThreadMap();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(map);
t.start();
}
Thread.sleep(5000);
byte[] c = new byte[1024 * 1024 * 2];
System.out.println("---c---");
}
分析:
先来看ThreadMap这个类里面有两个成员变量,a,b,他们有什么区别呢?
b.set(new byte[1024 * 1024 * 1]);
a.put(“1”,new byte[1024 * 1024 * 1]);
先是5个线程拥有同一个对象map。
为什么b是一个公共变量,存入的数据却是每个线程独立拥有的呢?
就看源码了呗!
public void set(T value) {
//看到了吗?t是个当前线程,不懂Thread.currentThread这个的可以去看看,不然真不能理解ThreadLocal
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Thread.currentThread();看到这个了嘛,这个表示当前线程,只和线程有关。
Thread t = new Thread(map);这里new了5个线程,那么就有5个Thread.currentThread();
来,我们尝试一下改变WeakHashMap变的和ThreadLocal差不多的功能。
a.put(Thread.currentThread().getName(),new byte[1024 * 1024 * 1]);
这样就OK了吗,每个线程进来用当前线程的名字做为key。
获取的时候使用a.get(Thread.currentThread().getName())就OK了啊。这样是不是更能理解ThreadLocal了。
http://www.cnblogs.com/skywang12345/p/3311092.html