ThreadLocal

ThreadLocal

  • 对ThreadLocal的理解
  • ThreadLocal的工作原理
  • ThreadLocal的应用场景
  • ThreadLocal使用Demo
  • ThreadLocal构成图

对ThreadLocal的理解

通常我们创建变量是可以被任何一个线程访问并修改的,如果想要实现每个线程都有自己的专属本地变量就需要使用到ThreadLocal了。ThreadLocal解决了多线程带来的数据资源竞争问题,解决线程数据的隔离问题,这是因为ThreadLocal就是让每个线程绑定自己的值。

如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,之后可以通过get(),set()方法来获取线程自己的本地副本的值,从而避免了线程安全问题

ThreadLocal最终的变量是存放在当前线程的ThreadLocalMap中的,ThreadLocalMap是一个只包含ThreadLocal和Object的Entry数组,其中Object就是我们要存储的变量

ThreadLocal的工作原理

ThreadLocal存储和获取线程本地变量主要通过get()和set()方法

当threadLocal调用自身的get()方法的时候

  1. 通过Thread.currentThread()获取到当前的线程Thread
  2. 通过调用getMap()方法且以当前的线程Thread作为入参,获取该线程的ThreadLocalMap
  3. 如果当前线程已经给ThreadLocalMap进行初始化了,直接从map中获值
  4. 否则的话调用对setInitialValue()方法对ThreadLocalMap 进行初始化赋值,并返回初始化的值 null
  5. setInitialValue()里面有一个createMap(),这就是对ThreadLocalMap初始化的过程
public T get() {
    // 获取到当前的线程Thread
    Thread t = Thread.currentThread();
    // 通过当前的线程获取该线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // 如果当前线程已经给ThreadLocalMap进行初始化了,直接从map中获值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 对ThreadLocalMap 进行初始化赋值
    return setInitialValue();
}

private T setInitialValue() {
    ...
    createMap(t, value);
    return value;
}

// 初始化ThreadLocalMap
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

当threadLocal调用自身的set()方法的时候

  1. 通过Thread.currentThread()获取到当前的线程Thread
  2. 通过调用getMap()方法且以当前的线程Thread作为入参,获取该线程的ThreadLocalMap
  3. 如果当前线程已经给ThreadLocalMap进行初始化了,直接从map中获值
  4. 否则的话调用对ThreadLocalMap进行初始化
public void set(T value) {
    // 获取到当前的线程Thread
    Thread t = Thread.currentThread();
    // 通过当前的线程获取该线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // 如果当前线程已经给ThreadLocalMap进行初始化了,直接从map中获值
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

1. 从Map中获取值首先是会根据 ThreadLocal 的散列值,查找对应元素在数组中的位置
2. 然后从该位置开始向后开始寻找合适位置
3. 如果数组中存在key,直接覆盖旧值
4. 如果发现数组中该位置的key是null,但是存在value,说明之前的ThreadLocal被回收了,需要使用新的元素替换旧的元素
5. ThreadLocal对应的key实例在Map中不存在也没有之前的陈旧元素,在该位置添加一个Entry
6. 数组长度 +1,然后清理ThreadLocal被回收的Entry
7. 如果没有这种Entry并且数组中的元素还超过了阀值,就会进行rehash
private void set(ThreadLocal<?> key, Object value) {

    Entry[] tab = table;
    int len = tab.length;
    // 根据 ThreadLocal 的散列值,查找对应元素在数组中的位置
    int i = key.threadLocalHashCode & (len-1);
    // 采用“线性探测法”,寻找合适位置
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        // key 存在,直接覆盖
        if (k == key) {
            e.value = value;
            return;
        }

        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

ThreadLocal的应用场景

  1. 多线程高并发情况下,使用同步会影响性能,可以使用 ThreadLocal
  2. 数据库连接、Session管理

ThreadLocal使用Demo

public class ThreadLocalTest implements Runnable {

    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        Student student = MyThreadLocal.getStudentWithThreadLocal();
        int age = student.getAge();
        for (int i = 1; i < 5; i++) {
            System.out.println(Thread.currentThread().getId() + "," + currentThreadName + "," + student.getAge());
            student.setAge(student.getAge() + i);
            System.out.println(Thread.currentThread().getId() + "," + currentThreadName + "," + student.getAge());
        }
    }

    /*public void run() {
        String currentThreadName = Thread.currentThread().getName();
        currentThreadName = currentThreadName + "_NO_THREAD_LOCAL";
        Student student = MyThreadLocal.getStudentWithoutThreadLocal();
        for (int i = 1; i < 5; i++) {
            System.out.println(Thread.currentThread().getId() + "," + currentThreadName + "," + student.getAge());
            student.setAge(student.getAge() + i);
            System.out.println(Thread.currentThread().getId() + "," + currentThreadName + "," + student.getAge());
        }
    }*/

    public static void main(String[] args) {
        ThreadLocalTest t = new ThreadLocalTest();
        Thread t1 = new Thread(t, "Thread A");
        Thread t2 = new Thread(t, "Thread B");
        t1.start();
        t2.start();
    }
}

// ##############################################

public class MyThreadLocal {

    private static final ThreadLocal<Student> studentThreadLocal = new ThreadLocal<Student>();

    public static Student getStudentWithThreadLocal() {

        System.out.println("studentThreadLocal:" + studentThreadLocal);

        //Thread.threadLocalMap 线程持有threadLocalMap对象
        Student student = studentThreadLocal.get();
        if (null == student) {
            student = new Student();
            studentThreadLocal.set(student);
        }
        return student;
    }

    private static Student student = null;
	public static Student getStudentWithoutThreadLocal() {
		if (null == student) {
			student = new Student();
		}
		return student;
	}
}

ThreadLocal构成图

ThreadLocal_第1张图片

你可能感兴趣的:(多线程学习笔记)