先强调一点:ThreadLocal不是用来解决共享变量问题的,它与多线程的并发问题没有任何关系。
1.基本概念?
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
2.threadLocal是如何做到为每一个线程维护变量的副本的呢?
在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本。
SimpleThreadLocal 原理实现
- public class SimpleThreadLocal {
- private Map valueMap = Collections.synchronizedMap(new HashMap());
- public void set(Object newValue) {
- //①键为线程对象,值为本线程的变量副本
- valueMap.put(Thread.currentThread(), newValue);
- }
- public Object get() {
- Thread currentThread = Thread.currentThread();
- //②返回本线程对应的变量
- Object o = valueMap.get(currentThread);
- //③如果在Map中不存在,放到Map中保存起来
- if (o == null && !valueMap.containsKey(currentThread)) {
- o = initialValue();
- valueMap.put(currentThread, o);
- }
- return o;
- }
- public void remove() {
- valueMap.remove(Thread.currentThread());
- }
- public Object initialValue() {
- return null;
- }
- }
ThreadLocal使用的例子:
- package com.baobaotao.basic;
- public class SequenceNumber {
- //①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
- private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
- public Integer initialValue(){
- return 0;
- }
- };
- //②获取下一个序列值
- public int getNextNum(){
- seqNum.set(seqNum.get()+1);
- return seqNum.get();
- }
- public static void main(String[ ] args)
- {
- SequenceNumber sn = new SequenceNumber();
- //③ 3个线程共享sn,各自产生序列号
- TestClient t1 = new TestClient(sn);
- TestClient t2 = new TestClient(sn);
- TestClient t3 = new TestClient(sn);
- t1.start();
- t2.start();
- t3.start();
- }
- private static class TestClient extends Thread
- {
- private SequenceNumber sn;
- public TestClient(SequenceNumber sn) {
- this.sn = sn;
- }
- public void run()
- {
- //④每个线程打出3个序列值
- for (int i = 0; i < 3; i++) {
- System.out.println("thread["+Thread.currentThread().getName()+
- "] sn["+sn.getNextNum()+"]");
- }
- }
- }
- }
执行结果:
thread[Thread-2] sn[1]
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]
可以看到,各个线程的longLocal值与stringLocal值是相互独立的,本线程的累加操作不会影响到其他线程的值,真正达到了线程内部隔离的效果。
3.使用场景
最常见的ThreadLocal使用场景为 用来解决数据库连接、Session管理等,那么接下来我们就看看spring事务中ThreadLocal的应用
4.总结
1、ThreadLocal能解决的问题,那肯定不是共享变量(多线程并发)问题,只是看起来有些像并发;像火车票、电影票这样的真正的共享变量的问题用ThreadLocal是解决不了的,同一时间,同一趟车的同一个座位,你敢用ThreadLocal来解决吗?
2、每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object