public static修饰的变量可以让所有线程共享,但是如果让每个线程有自己独享的数据对象,则使用java.lang.ThreadLocal类。他可以将线程和数据对象绑定在ThreadLocal的静态内部类ThreadLocalMap中,以实现线程独享数据对象。
《java并发编程实战》中提到两种情况下可用ThreadLocal将数据集与线程绑定:
package me.demo;
public class Accessor implements Runnable {
private final int id;
public Accessor(int id) {
this.id = id;
}
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){//不会清空"中断状态": return isInterrupted(false)
ThreadLocalVariableholder.increment();//TODO:1.为当前线程设置值;2.被操作的变量是static的
System.out.println(this);
Thread.yield();
}
}
@Override
public String toString() {
return "#"+id+": "+ThreadLocalVariableholder.get();
}
}
package me.demo;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @date 2017/12/12
*/
public class ThreadLocalVariableholder {
//静态变量,与类绑定
private static ThreadLocal value=new ThreadLocal(){
private Random random=new Random();
@Override
protected synchronized Integer initialValue() {//父类方法是没有synchronized关键字的
return random.nextInt();
}
};
//使用get()/set()实现数据对象+1
public static void increment(){
value.set(value.get()+1);
}
//返回数据对象的一个副本
public static int get(){
return value.get();
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService= Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new Accessor(i));
}
TimeUnit.SECONDS.sleep(1);
executorService.shutdown();
}
}
即便只有一个ThreadLocalVariableHolder对象,每个线程也仍有自己的数据对象。ThreadLocal保证不会出现竞态条件。
1)创建对象;
public ThreadLocal() {}
//由此可知我们可以使用 ThreadLocal t=new ThreadLocal()为其创建一个对象
2)为当前线程设置值;
/**
* 调用此方法的当前线程会与其数据集绑定;
* ThreadLocal创建对象时可以重写protected T initialValue()来初始化数据集,
* 而不用专门调用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);
}
3)获取当先线程绑定的数据集
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//根据当前对象获取其对应的value,即数据对象。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果当前线程没有绑定数据对象,则返回初始化值。
return setInitialValue();
}
4)为指定线程和数据对象创建映射,即将制定对象绑定到指定数据对象;
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
由上行代码可猜测:程序会为线程对象绑定数据集,而且初始化线程对象的属性threadLocals ,属性在ThreadLocal中如下所示:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
之所以所示猜测,是因为代码比自己想象的要复杂,其中createMap调用的构造函数代码如下:
/**
* 构造一个包含键值对的map。ThreadLocalMaps的对象构造是懒汉式的,所以
* 当我们有需要时才创建他。
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}