ThreadLocal变量

    ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    ThreadLocal类接口包含以下四个方法:(新版本的ThreadLocal已经支持泛型)    ThreadLocal<T>

        public T get()  : 返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用initialValue方法的返回值。

        protected T initialValue() :返回此线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时先调用此方法,但如果线程之前调用了set()方法,则不会对该线程在调用initialValue()方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove()方法,则可能再次调用此方法。该实现返回null;如果希望返回null以为的值,则必须为ThreadLocal创建子类,并重写该方法。通常将使用匿名内部类来完成此操作。

        public void remove() :移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用initialValue方法重新初始化其值。这将导致在当前线程多次调用initialValue方法。

        public void set(T value) :将此线程局部变量的当前线程副本中的值设置为指定的值。大部分子类不需要重写该方法,只需依靠initialValue方法来设置线程局部变量的值

   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Tread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的,且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

    ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

    概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。下面是一个ThreadLocal的应用实例:

public class SequenceNumber {
	
	private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){ 
		@Override
		public Integer initialValue(){
			return 0;
		}
	};
	
	public int getNextNum(){
		seqNum.set(seqNum.get()+1);
		return seqNum.get();
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SequenceNumber sn = new SequenceNumber();
		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(){
		  for (int i = 0; i < 3; i++) {
			System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]");
		}
	}

 }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值