ThreadLocal的作用

ThreadLocal的作用是给每个线程创建一个变量副本,它是线程的局部变量,可以在多线程的环境下相互之间不影响。

比如在Java中线程创建有哪几种方式 使用实现runnable接口 的方法创建的例子中,多个线程是共享一个count变量的,如下:

public class MyThreadTask implements Runnable {
    private int count = 10;
    @Override
    public void run() {
        while(count-- >0)
        {          
            System.out.println("线程名" + Thread.currentThread().getName() + ",次数(" + count + ")");
        }
    }
    public static void main(String[] args) {
		Runnable myTask = new MyThreadTask();
        
        Thread task1 = new Thread(myTask);
        Thread task2 = new Thread(myTask);
        task1.start();
        task2.start();
        
    }
}

测试一下

输出

线程名Thread-0,次数(9)
线程名Thread-0,次数(7)
线程名Thread-0,次数(6)
线程名Thread-0,次数(5)
线程名Thread-0,次数(4)
线程名Thread-0,次数(3)
线程名Thread-0,次数(2)
线程名Thread-0,次数(1)
线程名Thread-0,次数(0)
线程名Thread-1,次数(8)

 现在我们使用ThreadLocal创建线程的局部变量

public class ThreadLocalExample implements Runnable {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 10; // 这里设置期望的初始值
        }
    };
    @Override
    public void run() {
        while(threadLocal.get() >0)
        {
            threadLocal.set(threadLocal.get()-1);//减1
            System.out.println("线程名" + Thread.currentThread().getName() + ",次数(" + threadLocal.get() + ")");
        }
    }
    public static void main(String[] args) {
        Runnable myTask = new ThreadLocalExample ();

        Thread task1 = new Thread(myTask);
        Thread task2 = new Thread(myTask);
        task1.start();
        task2.start();

    }
}

测试一下

线程名Thread-0,次数(9)
线程名Thread-1,次数(9)
线程名Thread-0,次数(8)
线程名Thread-1,次数(8)
线程名Thread-0,次数(7)
线程名Thread-1,次数(7)
线程名Thread-0,次数(6)
线程名Thread-1,次数(6)
线程名Thread-0,次数(5)
线程名Thread-1,次数(5)
线程名Thread-0,次数(4)
线程名Thread-1,次数(4)
线程名Thread-0,次数(3)
线程名Thread-1,次数(3)
线程名Thread-0,次数(2)
线程名Thread-1,次数(2)
线程名Thread-0,次数(1)
线程名Thread-1,次数(1)
线程名Thread-0,次数(0)
线程名Thread-1,次数(0)

 从结果中看出线程之间threadLocal 值是相互不影响的,安全的。

ThreadLocal原理

ThreadLocal类中有一个map对象用于存储每个线程的变量副本。map中的key为线程对象,value为变量值。ThreadLocal的set函数

private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                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应用场景

常用于web服务器保存临时会话对象,我们知道web服务器是多线程的,比如tomcat,用户的每次请求都要保存到一个临时变量里,方便下次的调用获取,那么此时就需要ThreadLocal了。