多线程学习笔记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
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值