【JUC】15-ThreadLocal线程局部变量

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CRE_MO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值