栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ThreadLocal及强软弱虚引用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ThreadLocal及强软弱虚引用

1.threadLocal
static ThreadLocal tl = new ThreadLocal();
  • 上述threadLocal在创建的时候是在各自的线程中创建一个person对象
  • 疑问:为什么tl.get()获取不到person呢?
  • 用途:
    1. 声明式事务的时候,有多个数据库连接,我们将多次连接操作表示一个事务,那么我们怎么获得每个会话的同一个连接呢,那么连接放到threadlocal中,下次不在从连接池中取,而是从threadLocal中取
    2. 拦截器中将当前登录用户的基本信息放到threadLocal中,方便全局调用
1.set方法
  • Thread.currentThread.map(ThreadLocal,person)
  • 设到了当前线程的map中
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

  1. getMap方法
//这里获取的map其实就是当前线程下的threadLocal的内部类threadLocalMap
//注意: 这里的threadlocals是当前线程下的
ThreadLocal.ThreadLocalMap threadLocals = null;

 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  1. createMap方法
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
2.get方法

返回此线程局部变量的当前线程副本中的值。 如果该变量对于当前线程没有值,则首先将其初始化为调用initialValue方法返回的值

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();
    }

setInitialValue方法

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
2.Java的四种引用
public class M {
    //垃圾回收时会调用finalize方法,如果我们重写这个方法可以看到回收时打印对应的信息
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize");
    }
}
1. 强引用
  • 概念:强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OOM,也不会靠随意回收具有强引用的对象来解决内存不足的问题
  • 场景:基本的对象使用
public class NormalReference {
    public static void main(String[] args) throws IOException {
        //强引用就是普通的引用,只要指向他就不会被回收,如果m为空则会被回收
        M m = new M();
        m = null;
        System.gc(); //DisableExplicitGC
        System.out.println(m);

        System.in.read();//阻塞main线程,给垃圾回收线程时间执行
    }
}
2.软引用
  • 概念: 一个对象只有一个软引用指向的时候在只有系统内存不够用的时候才会回收
  • 场景:实现内存敏感的高速缓存
public class softReference {
    //软引用非常适合缓存使用
    //一个对象只有一个软引用指向的时候在只有系统内存不够用的时候才会回收,内存够用的时候是不会回收的
    public static void main(String[] args) {
        SoftReference m = new SoftReference<>(new byte[1024 * 1024 * 10]);
        //m = null;
        System.out.println(m.get());
        System.gc(); //尝试进行垃圾回收
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m.get());

        //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[1024 * 1024 * 12];
        System.out.println(m.get());
    }
}
3.弱引用
  • 概念:一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象

  • 场景:一般用在容器里,ThreadLocal类的静态内部类ThreadLocalMap的静态内部类Entry中的k,就是弱引用

  • 疑问:为什么Entry要是用弱引用?

    如果是强引用,即使tl=null,但key的引用依然指向ThreadLocal对象,所以会有内存泄漏,而使用弱引用则不会

    但是还是存在内存泄漏的问题,ThreadLocal被回收后key值变为null,则导致整个value无法访问到,因此还是存在

    存泄漏的问题,所以我们一般时候ThreadLocal的时候会在不适用的时候调用remove方法

public class WeakReference {
    public static void main(String[] args) {
        WeakReference m = new WeakReference<>(new M());
        
        System.out.println(m.get());
        System.gc();
        SleepHelper.sleepSeconds(1);
        System.out.println(m.get());
		
        ThreadLocal tl = new ThreadLocal<>();
        tl.set(new M());
        tl.remove();//释放内存
    }
}
4.虚引用
  • 概念:“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
  • 场景:虚引用主要用来跟踪对象被垃圾回收器回收的活动,且虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。总之就是通过队列来得到一个通知, 主要用来管理堆外内存,当对象被回收时,通过Queue可以检测到然后清理堆外内存(Unsafe类).

public class PhantomReference {
    private static final List LIST = new linkedList<>();
    private static final ReferenceQueue QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        //一旦虚引用被回收,会装到队列里,则可以判断这个引用被回收了
        PhantomReference phantomReference = new PhantomReference<>(new M(), QUEUE);
        System.out.println(phantomReference.get());

        ByteBuffer b = ByteBuffer.allocateDirect(1024);

        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                SleepHelper.sleepSeconds(1);
                //无论怎么get都是空值,虚引用get不到(弱引用能)
                System.out.println(phantomReference.get());
            }
        }).start();

        //垃圾回收线程
        new Thread(() -> {
            //不断检测队列中是否有值
            while (true) {
                Reference poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
                }
            }
        }).start();

        SleepHelper.sleepSeconds(1);

    }
}


转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号