浅谈ThreadLocal原理

一、ThreadLocal是什么?

         从字面上看“Threadlocal”表示“Thread”的“Local”,其实不然,他并非是线程的本地实现版本,也不是一个Thread,而是Threadlocalvariable(线程局部变量)。线程局部变量其实功能非常简单,就是为每个使用该变量的线程提供一个点亮值的副本,这是java中一种比较特殊的线程绑定机制,每一个线程都可以独立地改变自己的副本,不会和其他线程的副本冲突,不影响其他线程。

二、ThreadLocal API解释(JDK1.7)

        ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。       

        ThreadLocal提供了一个构造方法和get()、initialValue()、remove()、set(T)四个普通方法。

1、ThreadLocal() 构造方法:创建一个线程本地变量

2、get():返回此线程局部变量的当前线程副本中的值

112004_vcvd_3275321.png

代码很容易理解,首先我们通过Thread.currentThread得到当前线程,然后获取当前线程的threadLocals变量,这个变量就是ThreadLocalMap类型的。然后根据当前的ThreadLocal实例作为key,获取到Entry对象。

3、initialValue():返回侧线程局部变量的当前线程的“初始值”

4、remove():移除此线程局部变量当前线程的值

112119_UjDY_3275321.png

5、set(T value):将此线程局部变量的当前线程副本中的值设置为指定值

112105_pIIq_3275321.png

代码同样很容易理解。同样根据Thread.currentThread得到当前线程,如果当前线程存在threadLocals这个变量不为空,那么根据当前的ThreadLocal实例作为key寻找在map中位置,然后用新的value值来替换旧值。

       在ThreadLocal这个类中比较引人注目的应该是ThreadLocal->ThreadLocalMap->Entry这个类。这个类继承自WeakReference。关于弱引用的知识,等有空要好好研究一下。

三、应用示例

我们通过一个具体的实例了解一下ThreadLocal的具体使用方法


//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//        			 佛祖保佑       永无BUG     永不修改 			  //

package com.test.basic;

/**
 * @author GX
 *
 * @date 2017年2月24日 上午10:10:38
 */
public class SequenceNumber {
    
    //A 通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
		public Integer initialValue() {
			return 0;
		}
	};

    //B 获取下一个序列值
	public int getNextNum(Integer num) {

		threadLocal.set(threadLocal.get() + num);
		return threadLocal.get();
	}

	public static void main(String[] args) {
		SequenceNumber sNumber = new SequenceNumber();
        
        //C 4个线程共享sNumber,各自产生序列号
		TestClient t1 = new TestClient(sNumber, 3);
		TestClient t2 = new TestClient(sNumber, 5);
		TestClient t3 = new TestClient(sNumber, 7);
		TestClient t4 = new TestClient(sNumber, 9);
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}

	private static class TestClient extends Thread {
		private SequenceNumber eNumber;
		private Integer num;

		public TestClient(SequenceNumber sNumber, Integer num) {
			this.eNumber = sNumber;
			this.num = num;
		}

        //D 每个线程打出4个序列值
		public void run() {
			for (int i = 0; i < 4; i++) {
				System.out.println(
						"thread{" + Thread.currentThread().getName() + "} sn{" + eNumber.getNextNum(num) + "}");
			}
		}

	}

}

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中A处所示。TestClient线程产生一组序列号,在C处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:

114825_mGIO_3275321.png

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

 

 

 

转载于:https://my.oschina.net/u/3275321/blog/845413

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值