Java中ThreadLocal

1、什么是ThreadLocal

ThreadLocal:是 Thread 的局部变量不是线程,更不是本地线程的意思) ,是每个线程独享的本地变量,每个线程都有自己的 ThreadLocal,它们是线程隔离的。

例子:测试类 ThreadLocalTest2

package com.looper.day1.thread;

public class ThreadLocalTest2 {

    static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                System.out.println(threadName+" : "+threadLocal.get());
                threadLocal.set(0);
                System.out.println(threadName+" : "+threadLocal.get());
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                System.out.println(threadName+" : "+threadLocal.get());
                threadLocal.set(1);
                System.out.println(threadName+" : "+threadLocal.get());
            }
        });

        thread1.start();
        thread2.start();

    }

}

结果:

Thread-0 : null
Thread-0 : 0
Thread-1 : null
Thread-1 : 1

结论:说明 ThreadLocal 是线程隔离的,每个线程中得到的都是初始值,ThreadLocal 对象不会因为某一个线程改变值,而导致其他线程获取的值发生变化。


2、ThreadLocal的作用

ThreadLocal:用来解决多线程程序的并发问题。

当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

作用:为每个线程对象创建自己独立的的副本。


3、ThreadLocal类中的方法

void set(T value)

将此线程局部变量的当前线程副本中的值设置为指定值;

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

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

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

void remove()

移除此线程局部变量当前线程的值;

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

protected T initialValue()

返回此线程局部变量的当前线程的初始值;

protected T initialValue() {
    return null;
}

4、ThreadLocal的存储原理

ThreadLocal 是解决线程安全问题一个很好的思路,ThreadLocal 本身不存储任何值,ThreadLocal 类中有一个 Map,用于存储每一个线程的变量副本,Map 中元素的键为线程对象,而值对应线程的变量副本,由于 Key 值不可重复,每一个「线程对象」对应线程的「变量副本」,而到达了线程安全。


5、ThreadLocal使用场景

  • 场景1:每个线程需要一个独享的对象,通常是工具类,比如典型的SimpleDateFormat和Random等。
public class ThreadLocalTest1 {

    public static ExecutorService THREAD_POOL = Executors.newFixedThreadPool(10);
    static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            THREAD_POOL.submit(new Runnable() {
                @Override
                public void run() {
                    String date = new ThreadLocalTest1().date(finalI);
                    System.out.println(date);
                }
            });
        }
        // 关闭线程池,此种关闭方式不再接受新的任务提交,等待现有队列中的任务全部执行完毕之后关闭
        THREAD_POOL.shutdown();
    }

    private String date(int seconds) {
        // 参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
        Date date = new Date(1000 * seconds);
        String format;
        synchronized (ThreadLocalTest1.class){
            format = DATE_FORMAT.format(date);
        }

        return format;
    }

}

  • 场景2:每个线程内需要保存线程内的全局变量,这样线程在执行多个方法的时候,可以在多个方法中获取这个线程内的全局变量,避免了过度参数传递的问题。

比如网站的某个用户登录之后,每一个业务处理的时候都需要判断当前用户是不是会员,这个用户信息在登录之后,就希望他只一个全局的,可共享的。

单线程情况下,该用户信息可以为设为 static 没问题,进行串行处理;但是多线程或者并发情况下就会有问题,对这个前一个线程对用户信息 set 之后,下一个线程 get 到的用户信息,就不一定值最初的了。


6、总结

ThreadLocal 使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal 为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,但是大大减少了线程同步所带来性能消耗(空间换时间)(Synchronized 属于时间换空间),也减少了线程并发控制的复杂度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值