工作生活Java

ThreadLocal

2019-07-05  本文已影响0人  刘一一同学

1. 概述

ThreadLocal主要解决多线程并发访问导致数据不一致问题,它为每一个使用该变量的线程都提供一个变量值的副本,虽然这种方式耗费内存,但是大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

ThreadLocal 如何做到为每一个线程维护变量的副本?其实实现思路很简单,在ThreadLocal类中有一个ThreadLocalMap,用于存储每一个线程的变量的副本。

2. 实现原理

2.1 get()

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

其中 ThreadLocalMap 可以理解为定制化的HashMap,而ThreadLocal只是对其进行了封装,并传递变量值,通过get()获取当前线程局部变量的值。

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

实现原理和get()一样,内部由ThreadLocalMap管理数据,set时先检查map是否为空,如果不为空就直接保存数据,如果为空则先创建map再保存数据。

3. ThreadLocal内存泄露问题

ThreadLocalMap中使用的key为弱引用,而value是强引用。所以,如果ThreadLocal没有被外部强引用的情况下,在GC执行的时候会把key清理掉,而 value不会被清理。这样一来,ThreadLocalMap中就会出现key为null的Entry。假如不做任何措施的话,value可能永远无法被GC回收,这个时候可能会产生内存泄漏。
以上情况,ThreadLocalMap的实现已经考虑到了,在调用 set()get()remove()方法时,会清理key为null的记录。(我们在使用完ThreadLocal后,最好手动调用remove()

4. ThreadLocal和synchonized的区别

5. 使用示例

举例:将ThreadLocal比喻成存放数据的盒子,盒子中只可以存储盒子主人的私有数据。由于数据空间是隔离的,因此,多线程并发访问而互不影响,从而避免了线程安全的问题。

public class ThreadLocalDemo implements Runnable {

    // list 不是线程安全的,所以每个线程都要有自己独立的副本
    private static ThreadLocal<List> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        ThreadLocalDemo obj = new ThreadLocalDemo();
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(obj, "thread" + i);
            Thread.sleep(1000);
            t.start();
        }
    }

    @Override
    public void run() {
        List<String> list = new ArrayList<>();
        list.add(Thread.currentThread().getName());
        threadLocal.set(list);
        System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
        list.add("1");
        list.add("2");
        System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
    }
}

弱引用

如果一个对象只具有弱引用,那就属于可有可无的对象。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。垃圾回收器线程执行时会扫描它所管辖的内存区域,一旦发现只具有弱引用的对象,不管当前内存空间足够与否,都会将它回收。不过,由于垃圾回收期是一个优先级很低的线程,因此不一定很快就会发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用对象被垃圾回收后,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

上一篇 下一篇

猜你喜欢

热点阅读