ThreadLocal是保存线程本地化对象的容器,当运行于多线程环境的某个对象用ThreadLocal维护变量时,ThreadLocal为每个使用变量的线程分配一个独立的变量副本。所以每个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。
从线程的角度看,这个变量就像线程专有的本地变量,这也是类名中"Local"所要表达的意思。
class Entity { // 使用重写initialValue方法初始化值 // public ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() { // @Override // protected Integer initialValue() { // return 0; // } // }; // 使用lambda表达式初始化值 public ThreadLocal<Integer> seqNum = ThreadLocal.withInitial(() -> 0); public int getNextSeqNum() { seqNum.set(seqNum.get() + 1); return seqNum.get(); } } class ThreadDemo extends Thread { private Entity entity; public ThreadDemo(Entity entity) { this.entity = entity; } @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(getName() + "=====" + entity.getNextSeqNum()); } } } public class ThreadLocalDemo { public static void main(String[] args) { Entity entity = new Entity(); ThreadDemo threadDemo01 = new ThreadDemo(entity); ThreadDemo threadDemo02 = new ThreadDemo(entity); threadDemo01.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadDemo02.start(); } }
public class SimpleThreadLocal<T> { private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<>()); public void set(T val) { map.put(Thread.currentThread(), val); } public T get() { Thread thread = Thread.currentThread(); if (map.containsKey(thread)) { return map.get(thread); } else { T t = init(); map.put(thread, t); return t; } } public void remove() { map.remove(Thread.currentThread()); } public T init() { return null; } }
下面我们调用自己实现的ThreadLocal测试效果
class Entity01 { public SimpleThreadLocal<Integer> seqNum = new SimpleThreadLocal<Integer>() { @Override public Integer init() { return 0; } }; public int getNextSeqNum() { seqNum.set(seqNum.get() + 1); return seqNum.get(); } } class ThreadDemo01 extends Thread { private Entity01 entity; public ThreadDemo01(Entity01 entity) { this.entity = entity; } @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(getName() + "=====" + entity.getNextSeqNum()); } } } public class SimpleThreadLocalDemo { public static void main(String[] args) { Entity01 entity = new Entity01(); ThreadDemo01 threadDemo01 = new ThreadDemo01(entity); ThreadDemo01 threadDemo02 = new ThreadDemo01(entity); threadDemo01.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadDemo02.start(); } }
可以看到实现了同样的效果
对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化
ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象共享化
前者仅提供了一份变量,让不同的线程排队访问;而后者为每个线程都提供了一份变量,因此可以同时访问而互不影响。
源码地址