ThreadLocal 多线程访问同一变量解决方案

声明:是参考stamen的文章写的,文章中很多地方也是摘抄于他的 《学习Spring必学的Java基础知识(6)----ThreadLocal》文章,http://www.iteye.com/topic/1123824




一提到多线程的话就会想到一个问题,就是对同一变量访问的安全性和准确性,要解决这个问题其实也有很多的方法,像加同步锁,创建一个线程外部能够访问到的map等等吧,而ThreadLocal无疑是最好的一种解决方案,它不会像同步一样降低并发性也不会像建立一个外部map那样书写过多的代码。


现在让我们认识下ThreadLocal,我主要的目的还是要学会用,而不是讲解ThreadLocal的来源什么的。首先ThreadLocal类为我们提供的对我方法有4个分别是:

  • void set(Object value)
  • 设置当前线程的线程局部变量的值;
  • public Object get()
  • 该方法返回当前线程所对应的线程局部变量;
  • public void remove()
  • 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
  • protected Object initialValue()
  • 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的默认实现直接返回一个null。



至于应用,看下下面一段代码就能够明白,很简单

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()+"]");
			}
		}
	}
}



这里有个知识点要说下,就是这段代码:

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
		public Integer initialValue(){
			return 0;
		}
	};


这段代码其实是一个内部类,上面说过了initialValue()这个方法是为了子类重写的,这样的定义就是声明了一个匿名内部类,继承了ThreadLocal类并重写了initialValue()方法,因为这个子类是匿名的,你无法在后续的代码里引用,所以必须在定义时同时创建实例,他的作用就相当于这段代码。

class OutterClass {

    private static class MyThreadLocal extends ThreadLocal<Integer> {
        public Integer initialValue(){    
            return 0;    
        }   
    }

    private static MyThreadLocal seqNum = new MyThreadLocal();
}

猜你喜欢

转载自liuzl121.iteye.com/blog/1759404