您的位置:首页 > 编程语言 > Java开发

ThreadLocal详解

2016-03-08 11:33 591 查看
1.ThreadLocal是什么

在JDK1.2版本中就提供了java.lang.ThreadLocal,ThreadLocal为解决多线程并发提供了新的思路。
ThreadLocal并不是一个Thread,而是一个线程局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

2.ThreadLocal中的接口


ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
●void set(T value)
设置当前线程的线程局部变量的值。
●public T get()
该方法返回当前线程所对应的线程局部变量。
●public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
●public T initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

3.自己实现一个ThreadLocal:MyThreadLocal
package com.tqmall;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* Created by dingbao on 16/3/8.
*/
public class MyThreadLocal<T> {

//创建一个Map,key为线程对象,value为本线程的变量副本
private  Map<Thread,T > threadTMap = Collections.synchronizedMap(new HashMap<Thread, T>());

public void set(T value){
threadTMap.put(Thread.currentThread(),value);
}

public T get(){
Thread thread = Thread.currentThread();
T value = threadTMap.get(thread);
if(value == null && !threadTMap.containsKey(thread)){
value = initialValue();
threadTMap.put(thread,value);
}
return value;
}

public void remove(){
threadTMap.remove(Thread.currentThread());
}

protected T initialValue(){
return null;
}

}



4.使用自己实现的MyThreadLocal为每个线程产生一组序列号

package com.tqmall;

/**
* Created by dingbao on 16/3/8.
*/
public class SequenceNum {

//通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static MyThreadLocal<Integer> seqNum = new MyThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};

//获取下一个序列
public Integer getNextNum(){
seqNum.set(seqNum.get()+1);
return seqNum.get();
}

public static void main(String[] args){
SequenceNum sequenceNum = new SequenceNum();

Test test = new Test(sequenceNum);
Test test1 = new Test(sequenceNum);
Test test2 = new Test(sequenceNum);

test.start();
test1.start();
test2.start();

}

private static class Test extends Thread {

private SequenceNum sn;

public Test(SequenceNum sn) {
this.sn = sn;
}

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

}

运行结果为:
thread[Thread-1] sn[1]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[1]
thread[Thread-2] sn[3]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
若改为ThreadLocal运行结果也是一样的。
考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java ThreadLocal 线程