多线程与高并发学习六

ThreadLocal

简介:多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题,而ThreadLocal就能解决这一问题。

public class ThreadLocalTest {
    static ThreadLocal<Person> tl = new ThreadLocal<>();

    public static void main(String[] args) {

        new Thread(() -> {
            SleepHelper.sleepSeconds(1);
            tl.set(new Person("zhangsan"));
        }).start();

        new Thread(() -> {
            SleepHelper.sleepSeconds(2);

            System.out.println(tl.get());
        }).start();

    }

    static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }
    }
}

如上代码输出结果为null

在这里插入图片描述

原因为上面我们设置的值为线程独有的,设置的是自己线程才能访问到的Person,另外的线程也是去得到自己线程本地的Person

那么具体是如何做到的呢:

首先看ThreadLocal的set方法,
先拿到当前线程,然后再将值设置到map里面,然后map的key是当前ThreadLocal对象,
Thread.currentThread .map(ThreadLocal,person),设置到了当前线程的map中,然后就导致了另一个线程的map访问不到。
在这里插入图片描述

在这里插入图片描述
应用:spring的声明式事务,保证同一个Connection

强引用

就是普通的Object o = new Object(),对于普通的对象只要有指针指向它的内存地址,那么java垃圾回收就不会回收它,看如下例子首先写一个M类,重写它的finalize方法:

public class M {
    /**
     * 垃圾回收的时候会调用此方法,重写这个方法可以看到对象什么时候被回收了
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize");
    }
}

再加上如下代码:

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

        System.in.read();//阻塞main线程,给垃圾回收线程时间执行
    }
}

这里因为m一直有它的指向,所以是不会被垃圾回收器回收的,如果想让它被回收则需要让m的指向为空,所以把如上注释的m=null给取消,则能看到m对象的回收了,这就是强引用。

软引用

当有一个对象被软引用指向的时候,只有系统内存不够使用的时候才会回收它,如下代码首先通过软引用创建10m的内存空间,然后通过get方法去获得里面被软引用对象指向的地址,然后最后再创建一个大小为12m的对象。

public class SoftReference {
    public static void main(String[] args) {
        SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(sr.get());
        System.gc();//让垃圾回收运行
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sr.get());

        //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[1024 * 1024 * 12];
        System.out.println(sr.get());
    }
}

这里也顺便调了一下JVM的堆内存参数:
在这里插入图片描述
输出如下结果:
在这里插入图片描述
应用:用作缓存

弱引用

只要遭遇到GC就能被回收
作用:在于如果有另一个强引用指向了被这个弱引用指向的地址,只要这个强引用消失了,那么就可以不用管了,一般用在容器里面(WeakHashMap)。

ThreadLocal里面也会涉及到弱引用:

public class WeakReference {
    public static void main(String[] args) {

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

    }
}

如下图:
tl new 出来了一个threadlocal对象,然后再将Threadlocal对象扔进map的key里面,value则放的是M对象,如下源码:通过 map.set(this, value);调用然后往里面扔的是一个Entry
在这里插入图片描述
而这里的Entry继承了WeakReferencem,然后k调用父类的构造方法super(k),相当于new WeakReference<>(new k()),这里的k是一个弱引用,然后这个key就相当于通过一个弱引用指向了ThreadLocal对象。
在这里插入图片描述

如下是整体的ThradLocal的set流程:
内存泄漏(Memeory Leak): 是指的是内存里面的某些对象永远不会被回收
在这里插入图片描述
注意:通过如上图可以发现ThreadLocal是存在内存泄漏,所以如果当前ThreadLocal不用了的话则需要调用它的remove方法去把ThreadLocal里面存的value被释放出来。

虚引用

用于管理堆外内存的,当对象被回收时,通过Queue可以检测到,然后清理堆外内存。


public class PhantomReference {
    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);
        //pha ->new出来的pha的内存空间,同时这段空间通过虚引用关联了一个new出来的M以及一个引用队列
        //一旦回收虚引用,则将虚引用装进队列里面,并接收到一个通知
        
        //虚引用get里面的值一直拿不到
        System.out.println(phantomReference.get());
        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                SleepHelper.sleepSeconds(1);
                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();

        SleepHelper.sleepSeconds(1);

    }
}

输出结果如下:
在这里插入图片描述

并发容器

Vector HashTable ->自带锁,现在基本不用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值