一、java.lang.ThreadLocal<T>
理解:
一个ThreadLocal是一个中介或者工具,来完成这样的目标,一个线程关联一个数据,在线程内共享该数据。
然后通过ThreadLocal对象,和当前线程对象,可以唯一设置一个数据,获取该数据。
其实每一个线程内部都有一个容器ThreadLocalMap,类似于一个Map,存储key-value值。
这里的value就是数据,这里的key是ThreadLocal对象。
ThreadLocal实例 | 线程T1 | 线程T2 | 线程T3 |
tl1 | tl1:value1 | tl1:value1 | |
tl2 | tl2:value2 | tl2:value2 |
二、ThreadLocal有这样几个方法:
1.T get()
返回当前线程、当前ThreadLocal对象对应的那个数据。
实现是以ThreadLocal对象为Key去当前线程的容器map中去找数据,找到则返回;找不到则初始化一个,set进去,然后返回。
2.void set(T value)
给当前线程,当前ThreadLocal对象关联一个数据。
实现是以ThreadLocal对象为KEY,变量为VALUE存储到当前线程的map属性中去。
3.protected T initialValue()
get方法再找不到当前线程对应的变量时调用这个方法,用于初始化一个变量的值,默认是返回null。可以写一个子类继承ThreadLocal类,重写这个方法以返回需要的值。
4.void remove()
删除当前线程对应的变量
实现是删除map属性中当前线程的KEY-VALUE数据。
--------------------------------------------------------------------------------------------------------------
三、另一个思路实现该功能:
package tp.t1;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ThreadLocal{
//map属性,用于存储线程对象和变量的对应数据
private Map values = Collections.synchronizedMap(new HashMap());
//返回当前线程对应的那个变量
public Object get(){
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread)){
o = initialValue();
values.put(curThread, o);
}
return o;
}
//给当前线程关联一个变量
public void set(Object newValue){
values.put(Thread.currentThread(), newValue);
}
//初始化一个变量的值
public Object initialValue(){
return null;
}
}
四、测试
- 测试1
package tp.t1;
import tp.t1.SequenceNumber;
import tp.t1.TestThread;
import java.lang.ThreadLocal;
public class Test1 {
/*
* 起两个线程
* 每个线程给自己在ThreadLocal实例t1中关联的变量加1后显示出来,循环3次。
* 看看每个线程的值会不会互相影响?
* 答案是不会影响,每个线程关联的是自己的变量
*/
public static void main(String[] args){
Thread _t1 = new TestThread();
Thread _t2 = new TestThread();
_t1.start();
_t2.start();
}
}
class SequenceNumber {
//只需要一个ThreadLocal实例,这里定义在一个类的静态属性中,在各个线程中可以直接访问到。
private static ThreadLocal<Integer> tl = new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
//定义一个方法,用于各个线程给自己关联的变量值加1
public static int getNextNum(){
SequenceNumber.tl.set(SequenceNumber.tl.get() + 1);
return SequenceNumber.tl.get();
}
}
class TestThread extends Thread{
public void run(){
for(int i = 0;i < 3;i++){
int _num = SequenceNumber.getNextNum();
System.out.println("Thread["+Thread.currentThread().getName()+"]-num["+_num+"]");
}
}
}
Thread[Thread-0]-num[1]
Thread[Thread-1]-num[1]
Thread[Thread-0]-num[2]
Thread[Thread-1]-num[2]
Thread[Thread-0]-num[3]
Thread[Thread-1]-num[3]
- 测试2
package tp.t2;
import java.lang.ThreadLocal;
public class Test2 {
/*
* 起两个线程
* 每个线程在各自关联的变量是同一个实例,这时每个线程修改实例的内容,输出实例的内容。
* 看看每个线程在ThreadLocal对象中,关联的变量是不是copy出来的副本,是不是会互不影响?
* 答案是各个线程如果关联同一个变量,那么一个线程修改变量后,另一个线程关联的变量也变化了。
*/
public static void main(String[] args){
//一个ThreadLocal实例,存储的变量时AnObject类型的实例
ThreadLocal<AnObject> _tl = new ThreadLocal<AnObject>(){
public AnObject initialValue(){
return new AnObject();
}
};
//一个AnObject类型的实例
AnObject _anObj = new AnObject();
_anObj.s = Thread.currentThread().getName();
//关联当前主线程和这个变量
_tl.set(_anObj);
System.out.println("Thread["+Thread.currentThread().getName()+"]-string["+_tl.get().s+"]");
//起的第一个线程,修改变量的内容,关联变量
Thread _t1 = new TestThread(_tl,_anObj);
_t1.start();
//起的第二个线程,修改变量的内容,关联变量
Thread _t2 = new TestThread(_tl,_anObj);
_t2.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出最后这个变量的内容
System.out.println(_anObj.s);
}
}
class TestThread extends Thread{
private ThreadLocal<AnObject> tl;
private AnObject anObj;
public TestThread(ThreadLocal<AnObject> _tl,AnObject _anObj){
this.tl = _tl;
this.anObj = _anObj;
this.anObj.s = this.getName();
//放在这里去关联是不可以的,因为这里的Thread.currentThread()是主线程
//this.tl.set(this.anObj);
}
public void run(){
//当前线程关联变量
this.tl.set(this.anObj);
System.out.println("Thread["+this.getName()+"]-string["+this.tl.get().s+"]");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread["+this.getName()+"]-string["+this.tl.get().s+"]");
}
}
class AnObject extends Object{
String s = "";
}
Thread[main]-string[main]
Thread[Thread-0]-string[Thread-1]
Thread[Thread-1]-string[Thread-1]
Thread[Thread-0]-string[Thread-1]
Thread[Thread-1]-string[Thread-1]
Thread-1