多线程学习笔记5之ThreadLocal和强 软 弱 虚引用

ThreadLocal

ThreadLocal中有一个Map,为每个线程维护一个副本,保证每个线程中的数据进行隔离,下面我们看一个小程序

public class ThreadLocalDemo {

    static Person p = new Person();

    public static void main(String[] args) {

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(p.name);
        },"t1").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            p.name = "Mary";
        },"t2").start();
    }


    static class Person {

        public String name = "Jack";
    }
}

打印结果是Mary,事实上,我想要的结果还是Jack,不希望在t2线程中对变量的修改影响到p1线程的结果,那么就需要ThreadLocal来进行对线程进行隔离

public class ThreadLocalDemo2 {

    static ThreadLocal<Person> t = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(t.get());
        },"t1").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Person person = new Person();
            person.name = "Mary";
            t.set(person);
        },"t2").start();

    }

    static class Person {
        public String name = "Jack";
    }
}

这段代码执行的结果是null,如果不了解的人会好奇,明明是下面的t2先设置了Mary,为什么t1取的是null,那是因为ThreadLocal对线程进行了隔离

  • ThreadLocal的使用场景
    Spring声明式事务,保证数据连接是同一个connection
强引用

强引用 new关键字创建的实例,或者是Class.newInstance()创建的对象是强引用,强引用只有在引用设置为null的时候才会被垃圾回收器回收,代码如下

public class NormalReferenceDemo {
    public static void main(String[] args) throws IOException {
        M m = new M();
        m = null;
        System.gc(); //DisableExplicitGC

        System.in.read();
    }
}
SoftReference 软引用

软引用只有在内存不够的时候才会被垃圾回收器回收,使用场景 缓存,代码如下

public class SoftReferenceDemo {
    public static void main(String[] args) {
        SoftReference<byte[]> 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 * 15];
        System.out.println(m.get());
    }
}

WeakReference 弱引用

弱引用指向的弱引用对象,如果引用对象还有强引用指向它,一旦强引用不在了,垃圾回收时立刻回收该对象,使用场景"容器"(ThreadLocal源码),弱引用简单的使用代码

public class WeakReferenceDemo {
    public static void main(String[] args) {
        WeakReference<M> m = new WeakReference<>(new M());

        System.out.println(m.get());
        System.gc();
        System.out.println(m.get());


        ThreadLocal<M> tl = new ThreadLocal<>();
        tl.set(new M());
        tl.remove();
    }
}

ThreadLocal内部是这么执行的,如下图

在这里插入图片描述

虚引用
  • 虚引用管理堆外内存

简单使用代码

public class PhantomReferenceDemo {
    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();



    public static void main(String[] args) {


        PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);


        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(phantomReference.get());
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference<? extends M> poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

执行结果如图:

在这里插入图片描述

我们发现取的时候都是null,原因是虚引用被回收会装载到Queue队列里,但是我们自己去取虚引用指向的对象是取不到的,因为这块内存并不属于JVM中的堆内存来管,而是直接开辟了堆外内存,我们通过java去取堆外内存中的对象肯定是取不到的,可以看一下下面的图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用弱引用是Java中的两种引用类型它们在内存管理和垃圾回收方面有所不同。 引用是最常见的引用类型,它会阻止被引用对象被垃圾回收。只要存在引用指向一个对象,即使系统内存不足,垃圾回收器也不会回收该对象。我们通常使用的对象引用都是引用。在ThreadLocal中,ThreadLocal对象本身就是一个引用弱引用是一种比较引用类型,它允许被引用的对象在没有引用时被垃圾回收。当一个对象只被弱引用引用时,垃圾回收器在下一次回收时就会将这个对象进行回收,不会等到内存不足的时候。在ThreadLocal中,ThreadLocalMap的Entry对象的key就是弱引用,即ThreadLocal对象的弱引用ThreadLocal使用弱引用作为key的原因是为了避免内存泄漏。引用的话,即使ThreadLocal对象已经没有被使用,但是由于引用仍然存在,ThreadLocal对象无法被垃圾回收,从而导致内存泄漏。通过使用弱引用,当ThreadLocal对象没有其他引用时,就可以被垃圾回收,避免内存泄漏的问题。 如果不使用弱引用,我们可以将ThreadLocal对象设置为null,手动释放对ThreadLocal引用,从而让ThreadLocal对象能够被垃圾回收。但是这种方式需要我们手动管理,很容易出错。使用弱引用可以让垃圾回收器自动回收不再使用的ThreadLocal对象,更加方便和安全。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [引用弱引用Threadlocal](https://blog.csdn.net/a779868946/article/details/121458153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [ThreadLocal引用](https://blog.csdn.net/weixin_43847283/article/details/125470183)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值