并发编程------Threadlocal

Threadlocal

ThreadLocal是Thread的局部变量,用于编多线程程序。Threadlocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。



为什么要使用Threadlocal

从上面的定义可以了解到,他可以保证数据变成局部变量,这样也就保证了数据的不共享。那么使用方法是怎样的?

import sun.misc.Launcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.*;

/**
 * @author 龙小虬
 * @date 2021/5/6 11:17
 */
public class Test01 {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        threadLocal.set("1");
        System.out.println(threadLocal.get());
    }
}

从这里貌似看不出用处,他主要用于高并发状况下。

我们来看这两段代码:

import sun.misc.Launcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.*;

/**
 * @author 龙小虬
 * @date 2021/5/6 11:17
 */
public class Test01 {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private static int count;

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                count = finalI;
                try {
                    Thread.sleep(500);
                } catch (Exception e) {

                }
                System.out.println(Thread.currentThread().getName() + "," + count);
            }, i + "").start();
        }
    }
}

这一段代码我们可以看出来,肯定是存在线程安全的。因为共享变量会在休眠的这段时间不断更改。

运行结果可想而知

在这里插入图片描述
很明显,这与我们的预期结果不同。

import sun.misc.Launcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.*;

/**
 * @author 龙小虬
 * @date 2021/5/6 11:17
 */
public class Test01 {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private static int count;

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                threadLocal.set(finalI + "");
                try {
                    Thread.sleep(500);
                } catch (Exception e) {

                }
                System.out.println(Thread.currentThread().getName() + "," + threadLocal.get());
            }, i + "").start();
        }
    }
}

反观这段代码的运行结果并不会发生变化

在这里插入图片描述
这样就保证了,在高并发的情况下,各个线程不会阻塞,正常的运行,而且还不会发生线程不安全问题。这句话提到了不会阻塞,在前面学习多线程的时候,为了保证全局变量的安全问题,是采取了加锁(synchronized)的方法。

这里就提及一下,synchronized与Threadlocal的区别,在上面的案例可以看出来,Threadlocal是将数据存储了,所以可以看出差别

两者均可以解决线程安全问题
Threadlocal占用内存多,因为需要缓存数据
Threadlocal以空间换时间,来保证线程安全
synchronized以时间换空间,来保证线程安全

现在就来看看他的主要方法。



Threadlocal的主要api

set()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

这里可以看到他的存储方式是利用的ThreadLocalMap,首先获取到当前线程的ThreadLocalMap ,之后在使用Threadlocal对象作为key,存入数据。

remove()

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

get()

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

其实这些都不是重点。重点在于Threadlocal存在内存溢出的问题。



Threadlocal内存溢出问题

在了解为什么会内存溢出的问题之前,要了解几个概念。

  1. 弱引用
    调用方法WeakReference来创建,具体请查看可达性分析算法与四种引用
  2. 内存泄露
    指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

了解了这个之后,我们可以看看内部的ThreadLocalMap源码:
在这里插入图片描述
可以看到,Entry继承的是采用弱引用的Threadlocal,那么一旦内存不足,java中的弱引用在内存不足的时候会被回收掉,回收之后变成(null,value)的形式,key被收回掉了。后面获取的时候,也不会有Thread.currentThread()为null,那么这个key-value一直都不会被回收。可能就会造成内存溢出。那么这个问题是怎么解决的呢?

  1. 可以自己调用remove方法将不要的数据移除避免内存泄漏的问题
  2. 每次在做set方法的时候会清理之前 key为null的键值对


Threadlocal为什么采用弱引用而不是强引用

  1. 强引用:
    将ThreadLocal 的引用指向为null,但是每个线程中有自己独立ThreadLocalMap还一直在继续持有该对象,但是ThreadLocal 对象不会被回收,就会发生ThreadLocal内存泄漏的问题。

  2. 弱引用:
    将ThreadLocal 的引用指向为null,Entry 中的key指向为null,但是下次调用set()方法的时候,会根据判断如果key空的情况下,直接删除,有可能会发生Entry内存泄漏的问题。

所以说不管是用强引用还是弱引用都是会发生内存泄漏的问题,使用弱引用可以实行手动回收部分键值对,减少内存泄露的概率。

但是最终根本的原因Threadlocal内存泄漏的问题,产生于ThreadLocalMap与当前线程的生命周期一样,如果没有手动的删除的情况下,就有可能会发生内存泄漏的问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙小虬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值