ThreadLocal
本地线程变量,为当前线程填充的变量;
- 变量对其他线程而言是封闭且隔离的,ThreadLocal为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内容的副本变量;
方法
**T get()**:返回当前线程本地变量的当前线程的副本中的值;
**void remove()**:移除此线程的本地变量;
**void set()**:设置此线程本地变量的值;
<S> ThreadLocal<S>
:创建一个线程局部变量。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ThreadLocalStudy {
public static ThreadLocal<Apple> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) { new Thread(()->{ threadLocal.set(new Apple()); System.out.println(Thread.currentThread().getName()+":"+threadLocal.get()); },String.valueOf(i)).start(); } TimeUnit.SECONDS.sleep(3); }
} class Apple{}
|
1 2 3 4 5 6 7
| 0:com.yn.Apple@4f0c4707 2:com.yn.Apple@7e129604 1:com.yn.Apple@19281561 4:com.yn.Apple@4388eabf 3:com.yn.Apple@90472a2
Process finished with exit code 0
|
对于不同的线程,theradLocal中值不同。
原理
文字介绍
![image]()
描述:在Thread内部有个threadLocals引用,在使用ThreadLocal时,
- ThreadLocal会为threadLocals创建ThreadLocalMap的对象,
- 然后将自己的hash作为Map的key,将要存放的值作为value,存放在threadLocals里的table表里。
- 当要取用时,从Thread的threadLocals中,将自己的hash作为key,取出Entry对象,然后取出My Object。
内存泄露问题
在线程中,当一个ThreadLocal引用指向变成null,意味着这个ThreadLocal对象不再需要;
在threadLocals的table中,若key为null,value仍在堆中,在下次的ThreadLocalMap.set\get()方法执行都会清理漏掉的value;
但如果ThreadLocalMap.set\get()方法没有被执行,value一直未被清理,就有可能发生内存泄露问题。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| public class ThreadLocal<T> { public ThreadLocal() { } 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(); } public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
static class ThreadLocalMap { private Entry[] table; ... static class Entry extends WeakReference<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); } private Entry getEntry(ThreadLocal<?> key) {...} private void set(ThreadLocal<?> key, Object value) {...} private void remove(ThreadLocal<?> key) {...} ... } }
|
WeakReference 弱引用
https://brightloong.github.io/2018/05/27/%E5%85%B3%E4%BA%8EJava%E4%B8%AD%E7%9A%84WeakReference/#more
当一个对象仅仅被weak reference,而没有任何其他strong reference指向的时候;
如果gc运行,不论内存空间是否足够,这个对象都会被回收;
认识弱引用
WeakReference类继承Reference,有两个构造函数。
1 2 3 4 5 6 7 8 9 10 11 12
| public class WeakReference<T> extends Reference<T> { public WeakReference(T referent) { super(referent); } public WeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }
}
|
创建一个弱引用WeakReference对象,指向给定的对象T;
当给定的对象T被回收,WeakReference对象会被放到ReferenceQueue队列里面,若没有给定ReferenceQueue,则不会发生。
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class WeakReferenceStudy { public static void main(String[] args) throws InterruptedException { Bottle bottle = new Bottle(new Water("Pure Water")); System.out.println(bottle); System.out.println(bottle.get());
System.gc();
TimeUnit.SECONDS.sleep(5);
System.out.println(bottle); System.out.println(bottle.get()); } }
class Water{ String name; public Water(String name){ this.name = name; } public String getName(){ return name; }
@Override protected void finalize() throws Throwable { System.out.println(getName()+":我被回收了。"); super.finalize(); }
@Override public String toString() { return "Water{" + "name='" + name + '\'' + '}'; } }
class Bottle extends WeakReference<Water>{
public Bottle(Water referent) { super(referent); } }
|
1 2 3 4 5 6 7 8
| com.yn.Bottle@1b6d3586 Water{name='Pure Water'} Pure Water:我被回收了。 com.yn.Bottle@1b6d3586 null
Process finished with exit code 0
|
- 首先Bottle对象和Water对象正常输出;
- 然后进行一次gc,Water被回收了;
- 再次输出的时候,Bottle正常输出,Water变成null。
ReferenceQueue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class WeakReferenceStudy2 { public static void main(String[] args) throws InterruptedException { ReferenceQueue<Water2> referenceQueue = new ReferenceQueue<>();
WeakReference<Water2> reference1 = new WeakReference<Water2>(new Water2("pure water"),referenceQueue); WeakReference<Water2> reference2 = new WeakReference<Water2>(new Water2("dirty water"),referenceQueue); System.out.println(reference1); System.out.println(reference2); System.out.println(reference1.get()); System.out.println(reference2.get());
System.gc();
TimeUnit.SECONDS.sleep(4);
System.out.println(reference1.get()); System.out.println(reference2.get());
Reference<? extends Water2> reference; while ((reference = referenceQueue.poll()) != null){ System.out.println(reference); } } }
class Water2{ String name;
public String getName() { return name; }
public Water2(String name) { this.name = name; }
@Override public String toString() { return "Water2{" + "name='" + name + '\'' + '}'; }
@Override protected void finalize() throws Throwable { System.out.println(name+"被回收了"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| java.lang.ref.WeakReference@1b6d3586 java.lang.ref.WeakReference@4554617c Water2{name='pure water'} Water2{name='dirty water'} dirty water被回收了 pure water被回收了 null null java.lang.ref.WeakReference@1b6d3586 java.lang.ref.WeakReference@4554617c
Process finished with exit code 0
|
- 首先创建一个引用队列;然后创建两个弱引用对象,创建Water2对象,将Water2对象和referenceQueue一起传入构造方法;
- 在输出中,两个引用对象和Water2对象正常输出;
- 两个Water2被回收,输出中为null;
- 输出referenceQueue发现两个弱引用对象,与之前的一致。