1. ThreadLocal
ThreadLocal提供线程局部变量。每个线程在访问ThreadLocal实例的时候都有自己的、独立的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(用户ID或事务ID)与线程关联起来。
class Saler {
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->0);
public void add(int x) {
threadLocal.set(x+ threadLocal.get());
}
public int getInt() {
return threadLocal.get();
}
public void remove() {
threadLocal.remove();
}
}
public class demo01 {
public static void main(String[] args) {
Saler saler = new Saler();
try {
for(int i=0; i<5; i++) {
int nextInt = new Random().nextInt(100);
saler.add(nextInt);
System.out.println(saler.getInt());
}
} finally {
saler.remove();
}
}
}
ThreadLocal要及时清除。
class User {
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->0);
public void set() {
threadLocal.set(threadLocal.get()+10);
}
public int get() {
return threadLocal.get();
}
public void remove() {
threadLocal.remove();
}
}
public class demo02 {
public static void main(String[] args) {
User user = new User();
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try {
for(int i=0; i<10; i++) {
threadPool.submit(()->{
try {
user. set();
System.out.println(user.get());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
user.remove();
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool.shutdown();
}
}
}
2. ThreadLocalMap
ThreadLocalMap是一个以ThreadLocal为key,任意对象为value的Entry对象。
3. ThreadLocal弱引用
3.1 内存泄漏
不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
3.2 Reference
finalize()方法可以在垃圾收集器将对象从内存中清除出去之前做出必要的清理方法。
强引用:出现OOM也不会对该对象进行回收。即使该对象以后不用到也不会被回收。容易导致内存泄漏。
class MyObj {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObj is removed...");
}
}
public class demo03 {
public static void main(String[] args) {
MyObj myObj = new MyObj();
myObj = null;
// 人工开启gc
System.gc();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Completed");
}
}
软引用:当系统内存充足时,不会被回收。系统内存不足会被回收。应用大量读取图片,可以用Map存储图片地址和SoftReference的Bitmap。
class MyObj {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObj is removed...");
}
}
public class demo03 {
public static void main(String[] args) {
SoftReference<MyObj> myObj = new SoftReference<>(new MyObj());
System.gc();
System.out.println(myObj);
try {
byte[] bytes = new byte[20 * 1024 * 1024];
System.out.println(myObj);
} catch (Exception e) {
} finally {
System.out.println("completed");
}
}
}
弱引用:只要进行gc就会被回收。软引用
class MyObj {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObj is removed...");
}
}
public class demo03 {
public static void main(String[] args) {
WeakReference<MyObj> weakReference = new WeakReference<>(new MyObj());
System.out.println(weakReference);
System.gc();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(weakReference);
}
}
虚引用:用于监控通知使用,无法访问相应的对象。虚引用对象被回收会被放在ReferenceQueue中。
class MyObj {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObj is removed...");
}
}
public class demo03 {
public static void main(String[] args) {
MyObj myObj = new MyObj();
ReferenceQueue referenceQueue = new ReferenceQueue<>();
PhantomReference phantomReference = new PhantomReference<>(myObj, referenceQueue);
List<byte[]> list = new ArrayList<>();
new Thread(()->{
while (true) {
list.add(new byte[1 * 1024 * 1024]);
System.out.println(phantomReference.get() + " add ok");
}
}).start();
new Thread(()->{
while(true) {
Reference poll = referenceQueue.poll();
if (poll!=null) {
System.out.println(poll + "有需对象加入队列");
break;
}
}
}).start();
}
}
4. ThreadLocal弱引用
ThreadLocal本身作为一个key来让线程从ThreadLocalMap中获取value。从而实现线程之间的数据隔离。
为什么Entry要使用弱引用?
当func1执行完毕后,栈帧销毁强引用tl没有了。使用弱引用就大概率会减少内存泄漏风险(key为null需单独处理),可以让ThreadLocal对象在执行完毕后顺利被回收且key引用指向null。
虽然弱引用,保证key指向的ThreadLocal对象能被及时回收,但是v指向的value对象是需要ThreadLocalMap调用get、set时发现key为null时,才会去回收整个entry、value,因此弱引用不能100%保证内存不泄露。需要手动调用remove方法来删除ThreadLock对象。底层通过调用expungeStaleEntry( ) 实现清除废旧的entry。