细说ThreadLocal

0 概述

维持线程封闭性的一种更规范的方法是使用ThreadLocal,这个类使得访问某个变量的每个线程都拥有自己的局部变量,它独立于初始化副本。本文主要介绍ThreadLocal相关知识。

1 类方法介绍

T get() 返回此线程局部变量的当前线程副本中的值。
protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。
void remove() 移除此线程局部变量当前线程的值。
void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。

public class ThreadLocalTest {
  //定义ThreadLocal对象
  private static final ThreadLocal<Integer> LOCAL = new ThreadLocal<Integer>() {
    //初始化值,每个线程都会获取到这个值得副本(注意引用对象)
    @Override
    protected Integer initialValue() {
      return 0;
    }
  };

  public static void main(String[] args) {

    Thread[] threads = new Thread[10];
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          int num = LOCAL.get();
          num++;
          LOCAL.set(num);
          System.out.println(Thread.currentThread().getName() + "num:" + LOCAL.get());
        }
      });
    }
    //启动线程
    for (Thread thread : threads) {
      thread.start();
    }
  }
}

结果如下,从结果可以看出线程之间是相互独立的

Thread-1num:1
Thread-0num:1
Thread-2num:1
Thread-3num:1
Thread-4num:1
Thread-5num:1
Thread-6num:1
Thread-7num:1
Thread-8num:1
Thread-9num:1
public class ThreadLocalTest {
  private  static Number number=new Number();
  //定义ThreadLocal对象
  private static final ThreadLocal<Number> LOCAL = new ThreadLocal<Number>() {
    //初始化值,每个线程都会获取到这个值的副本(注意引用对象)
    @Override
    protected Number initialValue() {
      return number;
    }
  };

  public static void main(String[] args) {

    Thread[] threads = new Thread[10];
    for (int i = 0; i < threads.length; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          Number num = LOCAL.get();
          num.num++;
          LOCAL.set(num);
          System.out.println(Thread.currentThread().getName() + "num:" + LOCAL.get().num);
        }
      });
    }
    //启动线程
    for (Thread thread : threads) {
      thread.start();
    }
  }
  private static class Number
  {
    private int num=0;
  }
}

由于每个线程得到初始化变量副本是Number对象引用,所以输出结果是不可预期的.

Thread-2num:3
Thread-3num:4
Thread-0num:3
Thread-1num:3
Thread-5num:6
Thread-4num:5
Thread-6num:7
Thread-7num:8
Thread-8num:9
Thread-9num:10

2 实现原理

  • 每个线程的变量副本存储在哪里?
  • 变量的副本是怎么从共享变量赋值出来的?

    JDK 源码分析:


   public void set(T value) {
         //获取当前线程
        Thread t = Thread.currentThread();
        //获取Thread类 对象成员变量threadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        //创建一个ThreadLocalMap的map,这个map的key是ThreadLocal本身
            createMap(t, value);
    }
    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;
            }
        }
        //如果map为空返回初始化值
        return setInitialValue();
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
     void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

从源码可以每一个线程维护了一个独立的ThreadLocalMap映射表,key是ThreadLocal本身,Value是存储的对象,从而实现了线程之间的相互独立。

3 应用场景

ThreadLocal用于存放多线程中线程上下文信息,保存引用类型数据,避免引用类型的传递。如维持一个数据库的全局连接对象,从而避免调用每个方法的时候都要传递一个连接对象~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值