并发编程(9)threadLocal的用法

       本章我们将讨论ThreadLocal的使用,主要分以下几个章节:

.1)ThreadLocal是什么?

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

它并不是一个Thread,而是threadlocalvariable(线程局部变量,也叫线程本地变量)。

.2)ThreadLocal的使用

啥也不说,先看个例子

创建一个Person对象

public class Person {
	
	private int age;
	
	private String name;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
ThreadLocal处理:
public class PersonManager {

	private static ThreadLocal<Person> personContor = new ThreadLocal<Person>() {
		@Override
		protected Person initialValue() {
			Person p = new Person();
			p.setAge(19);
			p.setName("xgj");
			return p;
		}
	};
	
	private static Person getPerson(){
		return personContor.get();
	}
	
	@SuppressWarnings("unused")
	private void setPerson(Person p){
		personContor.set(p);
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread p1=new Thread(){

			@Override
			public void run() {
				Person p=PersonManager.getPerson();
				p.setAge(20);
				System.out.println("Person["+p.getAge()+","+p.getName()+"]");
			}
			
		};
		Thread p2=new Thread(){

			@Override
			public void run() {
				Person p=PersonManager.getPerson();
				p.setAge(21);
				System.out.println("Person["+p.getAge()+","+p.getName()+"]");
			}
			
		};
		Thread p3=new Thread(){

			@Override
			public void run() {
				Person p=PersonManager.getPerson();
				System.out.println("Person["+p.getAge()+","+p.getName()+"]");
			}
			
		};
		
		System.out.println(PersonManager.getPerson().getAge());
		p1.start();
		p2.start();
		p3.start();
		p1.join();
		p2.join();
		p3.join();
		System.out.println(PersonManager.getPerson().getAge());
	}

}
执行结果:
19
Person[20,xgj]
Person[21,xgj]
Person[19,xgj]
19
从结果我们可以看出,Main函数在线程p1,p2,p3执行前后Person对象的age值不变,p1,p2,p3获取Person值后修改了age的值,但是3个线程之间互不影响,操作的都是当前线程绑定的person对象。

那么ThreadLocal的原理具体是什么呢?

我们先来看看ThreadLocal的源码提供的几个方法

.1)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);
    }
从这个方法中,首先获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。我们再来看看ThreadLocalMap这个对象,ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取,我们可以发现,map.set(this,value),这里的this指向的就是ThreadLocal对象,而值就是你所设置的对象了。我们来看一下getMap和createMap方法的实现
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
ThreadLocal.ThreadLocalMap threadLocals = null;

而createMap实现为:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

我们还注意到ThreadLocalMap代码中有弱引用,如下:

 static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
大家请注意,之所以用的是弱引用,就是为了在ThreadLocal失去强引用的时候,TreadLocal对应的Entry能够在下次gc时被回收,回收后的空间能够得到复用,在一定的程度下能够避免内存泄露。

.2)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)
                return (T)e.value;
        }
        return setInitialValue();
    }
get方法通过当前线程获取到ThreadLocalMap,如果ThreadLocalMap不为空,就根据ThreadLocal key从ThreadLocalMap中获取value值,如果ThreadLocalMap不存在,则调用初始化方法setInitialValue(),我们来看一下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;
    }
在此方法中基本上跟set(T value)方法相呼应,请注意这行代码 T value =initialValue(); 这里调用了一个方法 initialValue(),我们下面来分析一下。

.3)initialValue()

来看一下源码实现,

protected T initialValue() {
        return null;
    }
本方法返回null,说明该方法应该由ThreadLocal子类重写,也就是有ThreadLocal实现类实现T的赋值。

       通过上面几个方法的分析,我们基本上了解了ThreadLocal的工作原理,每个线程都拥有一个ThreadLocalMap对象,其中ThreadLocal对象为key,value为线程共享对象的副本,这样没有线程访问的都是本线程的变量副本,从而实现了线程间的访问隔离。

.4)remove()

 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
将当前线程局部变量的值删除,目的是为了减少内存的使用,需要注意的是,当线程结束后,对应线程的局部变量会被垃圾自动回收,所以显示调用该方法进行线程局部变量并不是必须的操作,但是能加快内存回收速度。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值