ThreadLocal线程本地变量的超详细解析

              前段时间面试都遇到了有关ThreadLocal有关的问题,今天正好写到了多线程,干脆对ThreadLocal进行一个从简单理解,到使用,到源码理解的学习和分析。

              1.ThreadLocal是什么

              ThreadLocal正如他的中文翻译 线程本地变量所说,顾名思义,就是每个线程自己的本地变量。简单的来说,就是在多线程的程序中,我们一般会申请共享变量。给所有线程使用。但是很多情况下,每个线程会对共享变量进行改变。比如,一个String 类型的static变量,线程一将String 赋值为 “线程一,然后在跑其他的逻辑的时候,第二个线程将String 又改成了线程二”,那么就会出个问题,线程一跑到后面在对该String进行使用的时候,就发现值已经被改变了。

                       ThreadLocal的价值就体现了出来。就是在每个线程里面对变量单独记录保存。这样,线程二对String的改变就是对自己内部改变,不会影响别的线程中的String值。

              针对这个,写个代码进行了一个测试,方便理解。

              

package zxy.maven.product;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//
public class ThreadTest {
	// threadLocal Test
	public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    
	public static String baseString ="";
	
	
	public static ExecutorService  executorService=Executors.newFixedThreadPool(4);

	
	private static void setTLocal(String value) {
		threadLocal.set(value);
	}

	private static String getTlocal() {
		return "ThreadLocal保存:"+Thread.currentThread().getName()+" : "+ threadLocal.get();
	}
	
	private static void setBLocal(String value) {
		baseString=value;
	}

	private static String getBlocal() {
		return "Baseocal保存:   "+Thread.currentThread().getName()+" : "+baseString;
	}
	
	
	
	public static void main(String[] args) throws InterruptedException {

		Thread thread=new Thread(new Runnable() {
			public void run() {
				setTLocal("子线程一保存变量");
				try {
					//睡眠一秒,模拟在处理某些程序
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(getTlocal());
			}
		});
		
		Thread thread2=new Thread(new Runnable() {
			public void run() {
				setTLocal("子线程二保存变量");
				System.out.println(getTlocal());
			}
		});
		
		executorService.execute(thread);
		executorService.execute(thread2);
		
		setTLocal("主线程保存变量");
		System.out.println(getTlocal());
		
		Thread.sleep(1000);
		
		
		
		Thread thread3=new Thread(new Runnable() {
			public void run() {
				setBLocal("子线程一保存变量");
				try {
					//睡眠一秒,模拟在处理某些程序
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.err.println(getBlocal());
			}
		});
		
		Thread thread4=new Thread(new Runnable() {
			public void run() {
				setBLocal("子线程二保存变量");
				System.err.println(getBlocal());
			}
		});
		
		executorService.execute(thread3);
		executorService.execute(thread4);
		
		setBLocal("主线程保存变量");
		System.err.println(getBlocal());	
	}	
}

           


                     看到结果,ThreadLocal保存的变量没有因为其他原因改变。但是Basecal的变了,因为当执行的时候,主线程执行完了输出,线程一将String 修改为“子线程一保存变量,进入睡眠,线程二进行修改,改String 为子线程二,这时候输出的时候,就变成了两个子线程二了。

                    如此就体现了ThreadLocal的价值,就是保证线程之间的变量处理互不影响。

                    

                   2.ThreadLocal是怎么实现的线程本地

                   从代码上来看看怎么实现了这个功能吧。
                    ThreadLocal内部主要的几个方法就是   public T get(), public void set(T value), public void remove(),分别是获取本地变量,保存本地变量和删除变量
                   从get方法开始看
                    public 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();
    }
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
               很简单,就是先获取当前正在执行的线程,获取当前执行的线程的ThreadLocalMap(每个Thread都有一个名字叫threadLocals  类型为ThreadLocal.ThreadLocalMap的变量) 。如果,当前Thread的 ThreadLocalMap不为空,即已经通过set或者initialValue初始化过,则直接返回有关对象类型。
               如果当前线程的ThreadLocalMap为空,则处理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;
    }
                       该方法的作用是对ThreadLocalMap进行初始化。首先如果有重写initialValue方法,则将重写的初始化对象给T,然后在尝试查一下当前线程ThreadLocalMap有没有值,有值则将初始化的值也set进去,没有值得话就创建一个新的map,把当前线程和T当做参数传递给createMap。
               
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
             createMap很简单,就是把当前线程的ThreadLocalMap用初始化的值保存一下。
               
              public 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);
    }
          看完了setInitialValue方法,其实这个就太简单了,只是把初始化的那个值换成了set 的value值,保存在了ThreadLocalMap里面,其他没有变化。

                    public void remove()
               
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
                   remove方法也非常简单,就是获取当前线程的 ThreadLocalMap,如果不为空的话,把他清除掉。
                   

                 看完了这些方法,基本上ThreadLocal也就完全明白了,其实还是比较简单的一个实现的东西。

               

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值