1.回顾
上节我们讲了线程共享范围内的共享变量,我们使用了一个Map(K,V),其中Key为正在执行的线程对象,value存储的是线程操作的变量,如一个int型的data,我们以key来区分是哪一个对象所操作的变量。这一节我们将使用ThreadLocal 这个线程范围内共享变量的专用API来实现。
2.使用ThreadLocal实现
2.1 使用ThreadLocal实现基本数据类型在线程范围内共享
使用ThreadLocal实现线程范围内的共享变量。代码如下:
public class ThreadShareData {
private static ThreadLocal<Integer> threadIntData = new ThreadLocal<Integer>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable(){
public void run() {
int data = (int) (Math.random()*100);
System.out.println(Thread.currentThread().getName() + " has modify data :" + data);
threadIntData.set(data);
new DataReadA().get();
new DataReadB().get();
}
}).start();
}
}
static class DataReadA{
public void get(){
int data = threadIntData.get();
System.out.println("DataReadA " + Thread.currentThread().getName()
+ " get data :" + data);
}
}
static class DataReadB{
public void get(){
int data = threadIntData.get();
System.out.println("DataReadB " + Thread.currentThread().getName()
+ " get data :" + data);
}
}
}
代码中我们声明了共享变量ThreadLocal threadIntData,线程内将使用这个threadIntData来保存各自要使用的数据,这里为一个随机整数data。
在线程的run方法里调用threadIntData.set(data);将当前线程内使用的数据备份到threadIntData里,由于这里有2个线程,那么threadIntData内部将保存两个数据副本,它们在线程内是独立的,互不干扰。由于线程范围内的变量是独立的,所以DataReadA和DataReadB模块读出来的data是它们所运行的线程里操作的data,如线程1读出来的data是它当时在run方法里set进去的data值,同理线程2读取的是它自己的data值。最后我们贴出运行日志:
Thread-1 has modify data :59
Thread-0 has modify data :28
DataReadA Thread-0 get data :28
DataReadA Thread-1 get data :59
DataReadB Thread-0 get data :28
DataReadB Thread-1 get data :59
我们观察上述日志,2个线程确实读取的是各自的数据。
2.2 使用ThreadLocal实现多条数据在线程范围内的共享
我们在2.1中已知的使用ThreadLoca来实现一个int数据在线程内独立访问。那么,现在如果有多条数据呢,比如工作人员的姓名、年龄,2个字段如何使用ThreadLoca来实现线程范围内的共享变量,并且2个线程内访问的是各自所拥有的学生信息。
第1步
创建一个Developer类(表示开发人员),它有姓名和年龄2个成员变量。
static class Developer{
String name;
int age;
public String getName() {
String name = threadDeveloper.get().name;
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
int age = threadDeveloper.get().age;
return age;
}
public void setAge(int age) {
this.age = age;
}
}
第2步
在Developer类内部定义一个ThreadLocal变量,用来存储线程独立的Developer对象
第3步
在Developer类内部定义一个获取实例,存储在ThreadLocal里的静态函数
static ThreadLocal<Developer> threadLocalDeveloper = new ThreadLocal<Developer>();
private Developer(){}
static public Developer getThreadInstance(){
Developer developerInstance = threadLocalDeveloper.get();
if(developerInstance == null){
developerInstance = new Developer();
threadLocalDeveloper.set(developerInstance);
}
return developerInstance;
}
上述getThreadInstance函数里先调用threadDeveloper.get()获取该线程拥有的那个Developer对象副本,如果为null,则new一个Developer对象,并存储到threadLocalDeveloper里。 这里的threadLocalDeveloper里存储的是与当前正在运行的线程相关的Developer实例,所以现在的2个线程通过共享threadLocalDeveloper,为各自都存储了一个独立的Developer副本。
最后,跟上节一样,我们读出与当前正在运行的线程相关的那一份Developer实例,这样就相当于读出了多条数据,而且与其它线程互不干扰,数据的值是私有的。完整的代码如下:
public class ThreadShareData {
private static ThreadLocal<Integer> threadIntData = new ThreadLocal<Integer>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable(){
public void run() {
Developer developer = Developer.getThreadInstance();
int age = (int) (Math.random()*100);
developer.age = age;
developer.name = age+"gxw";
//读取
DataReadA dataReadA = new DataReadA();
dataReadA.get();
DataReadB dataReadB = new DataReadB();
dataReadB.get();
}
}).start();
}
}
/**
* A模块
* @author xw.gao
* 读取线程内使用的变量data
*/
static class DataReadA{
public void get(){
Developer developer = Developer.getThreadInstance();
int age = developer.getAge();
String name = developer.getName();
System.out.println("DataReadA " + Thread.currentThread().getName()
+ "age :" + age+",name="+name);
}
}
/**
* B模块
* @author xw.gao
* 读取线程内使用的变量data
*
*/
static class DataReadB{
public void get(){
Developer developer = Developer.getThreadInstance();
int age = developer.getAge();
String name = developer.getName();
System.out.println("DataReadB " + Thread.currentThread().getName() + "age :" + age+",name="+name);
}
}
static class Developer{
String name;
int age;
static ThreadLocal<Developer> threadDeveloper = new ThreadLocal<Developer>();
private Developer(){}
static public Developer getThreadInstance(){
Developer developerInstance = threadDeveloper.get();
if(developerInstance == null){
developerInstance = new Developer();
threadDeveloper.set(developerInstance);
}
return developerInstance;
}
public String getName() {
String name = threadDeveloper.get().name;
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
int age = threadDeveloper.get().age;
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
运行日志如下:
DataReadA Thread-0age :10,name=10gxw
DataReadA Thread-1age :56,name=56gxw
DataReadB Thread-1age :56,name=56gxw
DataReadB Thread-0age :10,name=10gxw
我们发现线程0和线程1读取的Developer对象是互不干扰的,独立的,即实现了线程范围内共享变量,存储和读取多个独立字段的功能。