示例:
package javaplay.thread.test;
import java.util.Random;
public class ThreadLocalTest {
// 相当于一个hashmap,数据是线程范围内的,一个线程一个数据,特别简单
static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data : " + data);
x.set(data);// 以当前线程相关存
new A().get();
new B().get();
}
}).start();
}
}
static class A {
public void get() {
int data = x.get();// 以当前线相关取
System.out.println("A from " + Thread.currentThread().getName() + " get data : " + data);
}
}
static class B {
public void get() {
int data = x.get();
System.out.println("B from " + Thread.currentThread().getName() + " get data : " + data);
}
}
}
output:
Thread-0 has put data : 34039172
Thread-1 has put data : -1214045336
A from Thread-1 get data : -1214045336
A from Thread-0 get data : 34039172
B from Thread-1 get data : -1214045336
B from Thread-0 get data : 34039172
假设我们有多个线程需要范围内的共享数据,那么我们的代码就得改成下面的形式了:思路是把 其他属性的值打包成一个类或者使用多个ThreadLocal类,因为一个只能放一个数据
package javaplay.thread.test;
import java.util.Random;
public class ThreadLocalTest {
// 相当于一个hashmap,数据是线程范围内的,一个线程一个数据,特别简单
static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data : " + data);
x.set(data);// 以当前线程相关存
MyThreadScopeData myData = new MyThreadScopeData();
myData.setName("name" + data);
myData.setAge(data);
myThreadScopeData.set(myData);
new A().get();
new B().get();
}
}).start();
}
}
static class A {
public void get() {
int data = x.get();// 以当前线相关取
System.out.println("A from " + Thread.currentThread().getName() + " get data : " + data);
MyThreadScopeData myData = myThreadScopeData.get();
System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());
}
}
static class B {
public void get() {
int data = x.get();
System.out.println("B from " + Thread.currentThread().getName() + " get data : " + data);
}
}
}
class MyThreadScopeData {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
output:
Thread-0 has put data : -1267206375
Thread-1 has put data : -1073718559
A from Thread-0 get data : -1267206375
A from Thread-1 get data : -1073718559
A from Thread-0 getMyData: name-1267206375,-1267206375
A from Thread-1 getMyData: name-1073718559,-1073718559
B from Thread-1 get data : -1073718559
B from Thread-0 get data : -1267206375
上面的代码虽然实现了功能,但是代码实现是很垃圾,一个线程有一个实例,那么多个线程就有多个实例,所以我们还要对代码进行修改,一种更优雅的方式如下:
package javaplay.thread.test;
import java.util.Random;
public class ThreadLocalTest {
// 相当于一个hashmap,数据是线程范围内的,一个线程一个数据,特别简单
static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data : " + data);
x.set(data);// 以当前线程相关存
// MyThreadScopeData myData = new MyThreadScopeData();
// myData.setName("name" + data);
// myData.setAge(data);
// myThreadScopeData.set(myData);
MyThreadScopeData.getThreadInstance().setName("name" + data);
MyThreadScopeData.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
static class A {
public void get() {
int data = x.get();// 以当前线相关取
System.out.println("A from " + Thread.currentThread().getName() + " get data : " + data);
// MyThreadScopeData myData = myThreadScopeData.get();
// System.out.println("A from " + Thread.currentThread().getName() +
// " getMyData: " + myData.getName() + "," + myData.getAge());
MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());
}
}
static class B {
public void get() {
int data = x.get();
System.out.println("B from " + Thread.currentThread().getName() + " get data : " + data);
MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());
}
}
}
class MyThreadScopeData {
private MyThreadScopeData() {
}
// 此时可不用synchronized因为ThreadLocal本来就是线程独立的,不存在两个线程去搞同一份数据
// 每个线程都操作自己线程相关的实例
public static /* synchronized */ MyThreadScopeData getThreadInstance() {
MyThreadScopeData instance = map.get();
if (instance == null) {
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
// private static MyThreadScopeData instance =null;// = new
// MyThreadScopeData();
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
output:
Thread-0 has put data : -560329700
Thread-1 has put data : 1731260003
A from Thread-1 get data : 1731260003
A from Thread-0 get data : -560329700
A from Thread-1 getMyData: name1731260003,1731260003
A from Thread-0 getMyData: name-560329700,-560329700
B from Thread-0 get data : -560329700
B from Thread-1 get data : 1731260003
B from Thread-0 getMyData: name-560329700,-560329700
B from Thread-1 getMyData: name1731260003,1731260003
在线程结束时可调用ThreadLocal.clear(),这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量,因为有时ThreadLocal变量可能会存有大量的数据(日程月累),对应于有很多线程的时候,它本身就是一个map;
补充:
虚拟机死亡时会有一个钩子,线程死亡时应该也有类似的机制,但目前还没有查到,或许jdk并没有提供;