一、ThreadLocal
ThreadLocal:解决每个线程绑定自己的值,存放线程的私有数据。
public class ThreadLocalTest {
public static ThreadLocal local = new ThreadLocal();
public static void main(String[] args) {
if(local.get() == null){
local.set("a");
}
System.out.println(Thread.currentThread().getName()+"的local值为"+local.get());
new Thread(() -> {
local.set("999");
},Thread.currentThread().getName()).start();
new Thread(() -> {
local.set("666");
},Thread.currentThread().getName()).start();
System.out.println("其他线程尝试修改"+Thread.currentThread().getName()+"的local值为"+local.get());
}
}
main的local值为a
其他线程尝试修改main后的local值为a
上述结果可以看到,我们在主线程 main 中给 ThreadLocal 复制为 "a" ,在其他线程中修改local值,并没有修改成功。
那ThreadLocal底层是怎么实现的呐?
ThreadLocal.ThreadLocalMap threadLocals = null;
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal的 set 方法中,先获取当前线程,通过 getMap 方法返回一个 ThreadLocalMap,
如果map不为空,则将当前 ThreadLocal 作为key,将参数value为值存入 map 中。
如果 map 为空,则新建一个以当前ThreadLocal 作为key,将参数value为值的 ThreadLocalMap 并赋值给t.threadLocals
二、四个引用
JVM 会自己管理内存(分配、释放),通过(Garbage Collector Thread) 垃圾收集器线程,根据jvm实现的策略来释放对象内存。但是jvm在进行垃圾收集的时候,需要判断哪些对象需要销毁,这与对象的引用有关。
1、 强引用 Strong Reference
强引用类型是我们平时写代码的时候最常用的引用,也就是 new 一个对象。
User user = new User(); //User就是强引用
user = null; //将这个引用指向空指针后,user就可以通过垃圾收集器进行销毁,释放内存了
2. 软引用 SoftReference
在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。
在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
实例:先将当前方法的JVM内存调整到 25M。
public static void main(String[] args) {
// 默认已经分配10M,还剩下不到15M
SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());
// 再分配一个15M的数组,heap将装不下,
// 这时候系统会自动进行垃圾回收,把软引用干掉
byte [] b = new byte[1024*1024*15];
System.out.println(m.get());
}
[B@4554617c
[B@4554617c
null
3. 弱引用 WeakReference
在 JDK1.2 之后,用java.lang.ref.WeakReference 类来表示软引用。
弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
WeakReference<M> m = new WeakReference<>(new M());
System.out.println(m.get());
// 遇到gc就回收
System.gc();
System.out.println(m.get());
}
com.fan.demo1.yinyong.M@4554617c
null
而我们上面说的ThreadLock底层就是用来弱引用:
ThreadLocal<M> tl = new ThreadLocal<>();
tl.set(new M());
tl.remove();
之前说过,ThreadLocal 底层使用的是一个ThreadLocalMap 来存储数据。而这个ThreadLocalMap使用的是 Entry,这个entry 继承了WeakReference,如下代码:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
如上图所说,如果key为null后,会导致value变成无法访问的垃圾数据导致内存泄漏,如果有大量这样的操作最终内存不能即使回收,导致oom。 所以,我们在使用完ThreadLocal后,手动将添加的数据删除,避免造成内存泄漏。
补充:jdk也提供了java.util.WeakHashMap这么一个key为弱引用的Map。
在JVM里一个对象没用了是指没有任何其他有用对象直接或者间接执行它,具体点就是在GC过程中它是GCRoots不可达的。 Jvm提供的 WeakReference 机制能让我们感知到一个对象是否已经变成了垃圾对象,某个WeakReference对象所指向的对象如果被判定为垃圾对象,Jvm会将该WeakReference对象放到一个ReferenceQueue里,我们只要看下这个Queue里的内容就知道某个对象还有没有用了。 WeakHashMap就是这么做的 。
4. 虚引用 PhantomReference
java.lang.ref.PhantomReference 类
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。
虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
引用队列(ReferenceQueue)
引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。
与软引用、弱引用不同,虚引用必须和引用队列一起使用。