一、ThreadLocal实现线程范围的共享变量
1、见下页的示意图和辅助代码解释ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。
2、每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。
二、代码实现
1、ThreadLocal的作用初识
/**
* @Title: ThreadScopeShareData.java
* @Package com.lh.threadtest.t5
* @Description: TODO
* @author Liu
* @date 2018年1月16日 下午4:38:55
* @version V1.0
*/
package com.lh.threadtest.t6;
import java.util.Random;
/***
*
* @ClassName: ThreadLocalTest
* @Description: ThreadLocal类及应用技巧
*
* ThreadLocal的作用类似于Map,只不过它的作用更加强大,使用起来更加简单
*
* 总结:
* 一个ThreadLocal代表一个变量,故其中只能放一个数据,若有两个变量都要实现线程内共享,则要定义两个
* ThreadLocal对象;如果有100个共享变量呢?那就先定义一对象来封装这100个变量,然后在ThreadLocal中
* 存储这一个对象。这样设计将更加优雅一些。
*
* @author Liu
* @date 2018年1月16日 下午5:56:31
*
*/
public class ThreadLocalTest {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
public static void main(String[] args) {
for(int i = 0; i < 2; i++){
new Thread(new Runnable() {
public void run() {
Integer data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()
+ " has put data:" + data
);
x.set(data);
new A().get();
new B().get();
new C().get();
}
}).start();
}
}
static class A {
void get(){
Integer data = x.get();
System.out.println("A from " + Thread.currentThread().getName()
+ " get data:" + data
);
}
}
static class B {
void get(){
Integer data = x.get();
System.out.println("B from " + Thread.currentThread().getName()
+ " get data:" + data
);
}
}
static class C {
void get(){
Integer data = x.get();
System.out.println("C from " + Thread.currentThread().getName()
+ " get data:" + data
);
}
}
}
2、ThreadLocal多个变量共享场合
/**
* @Title: ThreadScopeShareData.java
* @Package com.lh.threadtest.t5
* @Description: TODO
* @author Liu
* @date 2018年1月16日 下午4:38:55
* @version V1.0
*/
package com.lh.threadtest.t6;
import java.util.Random;
/***
*
* @ClassName: ThreadLocalTest
* @Description: ThreadLocal类及应用技巧
*
* ThreadLocal的作用类似于Map,只不过它的作用更加强大,使用起来更加简单
*
* 总结:
* 一个ThreadLocal代表一个变量,故其中只能放一个数据,若有两个变量都要实现线程内共享,则要定义两个
* ThreadLocal对象;如果有100个共享变量呢?那就先定义一对象来封装这100个变量,然后在ThreadLocal中
* 存储这一个对象。这样设计将更加优雅一些。
*
* @author Liu
* @date 2018年1月16日 下午5:56:31
*
*/
public class ThreadLocalTest2 {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
private static ThreadLocal<MyThreadScopeData> myData = new ThreadLocal<>();
public static void main(String[] args) {
for(int i = 0; i < 2; i++){
new Thread(new Runnable() {
public void run() {
Integer data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()
+ " has put data:" + data
);
x.set(data);
myData.set(new MyThreadScopeData("name" + data, data));
new A().get();
new B().get();
new C().get();
}
}).start();
}
}
static class A {
void get(){
Integer data = x.get();
MyThreadScopeData myThreadScopeData = myData.get();
System.out.println("A from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
static class B {
void get(){
Integer data = x.get();
MyThreadScopeData myThreadScopeData = myData.get();
System.out.println("B from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
static class C {
void get(){
Integer data = x.get();
MyThreadScopeData myThreadScopeData = myData.get();
System.out.println("C from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
}
class MyThreadScopeData{
private String name;
private int age;
public MyThreadScopeData(String name, int age) {
super();
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "MyThreadScopeData [name=" + name + ", age=" + age + "]";
}
}
3、ThreadLocal与类的单例结合使用
/**
* @Title: ThreadScopeShareData.java
* @Package com.lh.threadtest.t5
* @Description: TODO
* @author Liu
* @date 2018年1月16日 下午4:38:55
* @version V1.0
*/
package com.lh.threadtest.t6;
import java.util.Random;
/***
*
* @ClassName: ThreadLocalTest
* @Description: ThreadLocal类及应用技巧
*
* ThreadLocal的作用类似于Map,只不过它的作用更加强大,使用起来更加简单
*
* 总结:
* 一个ThreadLocal代表一个变量,故其中只能放一个数据,若有两个变量都要实现线程内共享,则要定义两个
* ThreadLocal对象;如果有100个共享变量呢?那就先定义一对象来封装这100个变量,然后在ThreadLocal中
* 存储这一个对象。
*
* 将ThreadLocal与类的单例结合起来,使得线程共享数据功能对外隐藏起来,外部不用关心内部实现的细节,也更加简洁,而且这样设计更加优雅。
*
* @author Liu
* @date 2018年1月16日 下午5:56:31
*
*/
public class ThreadLocalTest3 {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
public static void main(String[] args) {
for(int i = 0; i < 2; i++){
new Thread(new Runnable() {
public void run() {
Integer data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()
+ " has put data:" + data
);
x.set(data);
MyThreadData myThreadData = MyThreadData.getThreadInstance();
myThreadData.setName("name" + data);
myThreadData.setAge(data);
new A().get();
new B().get();
new C().get();
}
}).start();
}
}
static class A {
void get(){
Integer data = x.get();
MyThreadData myThreadScopeData = MyThreadData.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
static class B {
void get(){
Integer data = x.get();
MyThreadData myThreadScopeData = MyThreadData.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
static class C {
void get(){
Integer data = x.get();
MyThreadData myThreadScopeData = MyThreadData.getThreadInstance();
System.out.println("C from " + Thread.currentThread().getName()
+ " get data:" + data + " data2: " +myThreadScopeData
);
}
}
}
//ThreadLocal结合类的单例使用,使得共享数据变得更加优雅
class MyThreadData{
private MyThreadData(){}
// private static MyThreadData instance = null;
private static ThreadLocal<MyThreadData> map = new ThreadLocal<>();
public /*synchronized*/ static MyThreadData getThreadInstance(){
MyThreadData instance = map.get();
if(instance == null){
instance = new MyThreadData();
map.set(instance);
}
return instance;
}
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;
}
@Override
public String toString() {
return "MyThreadScopeData [name=" + name + ", age=" + age + "]";
}
}
三、使用场景
1、例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。
四、关键点
1、ThreadLocal的作用类似于Map,只不过它的作用更加强大,使用起来更加简单。
2、一个ThreadLocal代表一个变量,故其中只能放一个数据,若有两个变量都要实现线程内共享,则要定义两个ThreadLocal对象;如果有100个共享变量呢?那就先定义一对象来封装这100个变量,然后在ThreadLocal中存储这一个对象。
3、将ThreadLocal与类的单例结合起来,使得线程共享数据功能对外隐藏起来,外部不用关心内部实现的细节,也更加简洁,而且这样设计更加优雅。
4、遗留问题:线程死亡时,传入一个释放该线程对应ThreadLocal空间的执行线程?