ThreadLocal类及应用技巧

一、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空间的执行线程?

转载于:https://my.oschina.net/Howard2016/blog/1608070

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值