Java多线程总结(4)— 线程范围内数据操作的隔离及ThreadLocal类
来源:互联网 发布:seo研究中心seo8 编辑:程序博客网 时间:2024/05/21 07:56
1. Java内存模型
在java中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享(本文使用“共享变量”这个术语代指实例域,静态域和数组元素)。局部变量(Local variables),方法定义参数(java语言规范称之为formal method parameters)和异常处理器参数(exception handler parameters)不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。Java内存模型的抽象示意图如下:
下面通过简单的案例分析。需求:多个线程独立的操作变量sharedData,每个线程都可修改、获得该变量。现实现每个线程单独修改该变量,且更改的值只能在本线程内可见。
2. static方式实现
我们知道static变量保存在堆内存中,可以被多个线程所共享。将所要共享的变量用static修饰:
// 利用static实现线程间数据的共享private static int sharedData;public void staticShareValuable() { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { // 线程范围内修改共享变量 sharedData = new Random().nextInt(100); new ModelA().get(); new ModelB().get(); } }).start(); }}// 线程的A模块class ModelA { public void get(){ System.out.println("ModelA from " + Thread.currentThread().getName() + " 线程 " + " get data :" + sharedData); }}// 线程的B模块class ModelB { public void get(){ System.out.println("ModelB from " + Thread.currentThread().getName() + " 线程 " + " get data :" + sharedData); }}
运行输出:
ModelA from Thread-1 线程 get data :37ModelA from Thread-0 线程 get data :37ModelB from Thread-1 线程 get data :37ModelB from Thread-0 线程 get data :37
由结果可知,static这种方式并不能实现线程单独修改该变量,且更改的值只能在本线程内可见。其实这是显然的,只是为了说明:我们在多线程中使用static共享数据的时候一定要考虑这个问题!
3. Map方式模拟ThreadLocal基本原理
将数据保存在Map集合当中,将当前的Thread实例作为key,当前线程操作的变量作value,这样就实现了每个线程独立的操作变量。即线程操作数据的隔离。
// 模拟ThreadLocal的实现private static Map<Thread, Integer> context = new HashMap<Thread, Integer>();/** * 每个线程独立的实现更改sharedData,更改的值只能在本线程内可见 * 利用传统的static不能实现! */public void mapShareValuable() { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { // 线程范围内修改共享变量 int copySharedData = new Random().nextInt(100); // map集合中放入key-value context.put(Thread.currentThread(), copySharedData); new ModelA().get(); new ModelB().get(); } }).start(); }}// 线程的A模块class ModelA { public void get(){ // map集合中根据key(当前线程实例),取出当前线程所要操作的值 int sharedData = context.get(Thread.currentThread()); System.out.println("ModelA from " + Thread.currentThread().getName() + " 线程 " + " get data :" + sharedData); }}// 线程的B模块class ModelB { public void get(){ int sharedData = context.get(Thread.currentThread()); System.out.println("ModelB from " + Thread.currentThread().getName() + " 线程 " + " get data :" + sharedData); }}
运行输出:
ModelA from Thread-0 线程 get data :61ModelA from Thread-1 线程 get data :33ModelB from Thread-0 线程 get data :61ModelB from Thread-1 线程 get data :33
由运行结果可以看出:Thread-0线程的模块A和B获得的数据为Thread-0线程所修改的数据,两个线程单独修改变量,且更改的值只能在本线程内可见。其实这就是ThreadLocal简单原理(只是key值不同,ThreadLocal内部的ThreadLocalMap(a customized hash map)对象,key为ThreadLocal对象)。
4. ThreadLocal
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。如果共享的变量有多个,可以将其封装成对象,放入ThreadLocal中。例如Struts2的ActionContext需要保存当前请求线程的request、response,param,application以及action等等。下面的案例简单模拟了Struts2的ActionContext功能。作为上下文封装当前线程的请求Request操作、响应Response,在Action中可通过ActionContext获取这些对象。
/** * 模拟Struts2的ActionContext * Struts2的ActionContext需要保存当前请求线程的request、response,param,application以及action等等。 * @author markliu * */public class ThreadLocalTest { public static void main(String[] args) { // 模拟两次请求 for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { // 以下代码模拟Tomcat封装请求信息等并绑定到对应线程 Request request = new Request(); request.setRequestMsg(Thread.currentThread().getName() + "线程请求HelloWorld.action资源"); Response response = new Response(); response.setResponseMsg("向" + Thread.currentThread().getName()+"线程" + "返回资源"); ActionContext context = ActionContext.getContext(); context.setRequest(request); context.setResponse(response); // 调用Action的方法 new HelloWorldAction().execute(); } }).start(); } }}class HelloWorldAction { public String execute() { ActionContext context = ActionContext.getContext(); System.out.println(Thread.currentThread().getName()+"线程在" + "Action中获取的请求信息:" + context.getRequest().getRequestMsg() + "-->" + "Action中获取的响应信息信息:" + context.getResponse().getResponseMsg()); return "success"; }}/** * 由于保存的数据有多个,需要多个ThreadLocal,可以 * 将需要保存的request、response等信息封装成一个上下文对象 * @author markliu * */class ActionContext { private static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>(); // 封装实际的request,response等信息 private Map<String, Object> context = new HashMap<String, Object>(); public static String REQUEST = "com.xxx.request"; public static String RESPONSE = "com.xxx.response"; /** * 获取当前线程所对应的ActionContext * @return */ public static ActionContext getContext() { // 从ThreadLocal中获取当前线程对应的上下文对象 ActionContext instance = actionContext.get(); if(instance == null){ // 如果首次请求,为null instance = new ActionContext(); actionContext.set(instance); } return instance; } // 从ThreadLocal移除当前线程的相关联的上下文对象 public void remove() { actionContext.remove(); } public void setRequest(Request request) { context.put(REQUEST, request); } public Request getRequest() { return (Request) context.get(REQUEST); } public void setResponse(Response response) { context.put(RESPONSE, response); } public Response getResponse() { return (Response) context.get(RESPONSE); }}class Request { private String requestMsg; public String getRequestMsg() { return requestMsg; } public void setRequestMsg(String requestMsg) { this.requestMsg = requestMsg; }}class Response { private String responseMsg; public String getResponseMsg() { return responseMsg; } public void setResponseMsg(String responseMsg) { this.responseMsg = responseMsg; }}
运行结果如下:
由此可见,实现了不同的线程分别处理各自线程范围内的Request和Response。
注意:
1. 上面案例的ThreadLocal实例只有一个。
2. 当线程结束时,要调用ThreadLocal的remove()
方法,从thread-local variable中移除。
转载请注明出处:线程范围内的数据共享及ThreadLocal
- Java多线程总结(4)— 线程范围内数据操作的隔离及ThreadLocal类
- Java基础:多线程之线程范围内的数据共享ThreadLocal
- 多线程之线程范围内的数据共享ThreadLocal
- Java多线程数据隔离(ThreadLocal)
- (三) Java多线程详解之线程范围内共享变量及ThreadLocal类使用
- 多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)
- ThreadLocal实现线程范围内的数据共享
- 线程范围内的共享数据 ThreadLocal
- ThreadLocal-------线程范围内的数据共享
- Java基础——ThreadLocal实现线程范围内的数据共享
- ThreadLocal类实现线程范围内数据共享
- ThreadLocal线程范围内共享数据
- Java并发库(五、六、七):线程范围内共享数据、ThreadLocal、共享数据的三种方法
- ThreadLocal 线程间的数据隔离
- ThreadLocal实现:java线程范围内的共享数据,线程外独立
- java多线程:5、线程范围内的数据共享_ThreadLocal
- java多线程学习(三)---线程范围内数据共享
- 黑马程序员-线程范围内的数据共享之ThreadLocal
- log4j详解与实战
- Emacs 放大缩小图片
- UIScrollView实现循环滚动
- 程序员 身边的趣事
- ajax省市区三级联动
- Java多线程总结(4)— 线程范围内数据操作的隔离及ThreadLocal类
- ueditor1_4_3-utf8-jsp 配置实现上传图片的功能遇到的问题,改变图片上传请求地址
- Android初识-权限大全
- C语言 指针(下)
- 纯css3实现 正在加载 动画
- [Servlet&JSP] 部署描述设置
- 几种很有用的java库
- BZOJ1208: [HNOI2004]宠物收养所
- oracle表数据误删恢复