线程范围内共享变量,使用ThreadLocal API来实现

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对象是互不干扰的,独立的,即实现了线程范围内共享变量,存储和读取多个独立字段的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冉航--小虾米

希望得到您的鼓励和交流

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值