ThreadLocal中保存的数据只能被当前线程私有,不被其它线程可见

证明

声明一个全局的变量threadLocal,初始值为1,通过3个线程对其进行访问修改设置,理论上threadLocal的最终值应该是6,然而我们的输出结果是3,说明了threadLocal中存放的数据是各自线程私有的

package com.mmall.concurrency.example.threadLocal;

public class UseThreadLocal {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 1;
}
}; //运行3个线程
public void startThreadArray() {
Thread[] thread = new Thread[3];
for (int i = 0; i < thread.length; i++) {
thread[i] = new Thread(new MyThread(i));
} for (int i = 0; i < thread.length; i++) {
thread[i].start();
}
} private class MyThread implements Runnable {
int id; public MyThread(int i) {
id = i;
} @Override
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
Integer v = threadLocal.get();
v=v+id;
threadLocal.set(v);
System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());
}
} public static void main(String[] args) {
UseThreadLocal useThreadLocal = new UseThreadLocal();
useThreadLocal.startThreadArray();
}
}

结果

Thread-0:start
Thread-2:start
Thread-1:start
Thread-0:1
Thread-2:3
Thread-1:2

小应用

ThreadLocal结合过滤器和拦截器进行搭配使用,通过在过滤器HttpFilter设置ThreadLocal中的值,通过拦截器HttpInterceptor移除拦截器中的值

编写`ThreadLocal类,包含设置、获取、移除操作

package com.mmall.concurrency.example.threadLocal;

public class RequestHolder {

    private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();

    public static void add(Long id) {
requestHolder.set(id);
} public static Long getId() {
return requestHolder.get();
} public static void remove() {
requestHolder.remove();
}
}

编写过滤器HttpFilter类,通过在doFilter方法中对ThreadLocal进行存数据

package com.mmall.concurrency;

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; @Slf4j
public class HttpFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException { } @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
log.info("do filter, {}, {}", Thread.currentThread().getId(), request.getServletPath());
RequestHolder.add(Thread.currentThread().getId());
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() { }
}

编写ThreadLocalController类,在业务中可以获取到在过滤器HttpFilter中对ThreadLocal中存放的数据

package com.mmall.concurrency.example.threadLocal;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
@RequestMapping("/threadLocal")
public class ThreadLocalController { @RequestMapping("/test")
@ResponseBody
public Long test() {
return RequestHolder.getId();
}
}

编写拦截器HttpInterceptor类,在完成业务逻辑处理后,在拦截器类HttpInterceptorafterCompletion方法中移除我们在过滤器HttpFilter中对ThreadLocal设置的值

package com.mmall.concurrency;

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle");
return true;
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
RequestHolder.remove();
log.info("afterCompletion");
return;
}
}

编写springboot的启动类ConcurrencyApplication,实例化了FilterRegistrationBean

package com.mmall.concurrency;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @SpringBootApplication
public class ConcurrencyApplication extends WebMvcConfigurerAdapter{ public static void main(String[] args) {
SpringApplication.run(ConcurrencyApplication.class, args);
} @Bean
public FilterRegistrationBean httpFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new HttpFilter());
registrationBean.addUrlPatterns("/threadLocal/*");
return registrationBean;
} @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
}
}

启动springboot启动类,访问http://localhost:8080/threadLocal/test,控制台输出

本文由博客一文多发平台 OpenWrite 发布!

ThreadLocal小试牛刀的更多相关文章

  1. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  2. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  3. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...

  4. Threadlocal使用Case

    Threadlocal能够为每个线程分配一份单独的副本,使的线程与线程之间能够独立的访问各自副本.Threadlocal 内部维护一个Map,key为线程的名字,value为对应操作的副本. /** ...

  5. 多线程映射工具——ThreadLocal

    ThreadLocal相当于一个Map<Thread, T>,各线程使用自己的线程对象Thread.currentThread()作为键存取数据,但ThreadLocal实际上是一个包装了 ...

  6. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

  7. ThreadLocal<T>的是否有设计问题

    一.吐槽 ThreadLocal<T>明显是.NET从JAVA中来的一个概念,但是这种设计是否出现了问题. 很明显,在JAVA中threadLocal直接是Thread的成员,当然随着th ...

  8. 理解ThreadLocal —— 一个map的key

    作用: 当工作于多线程中的对象使用ThreadLocal维护变量时,threadLocal为每个使用该变量的线程分配一个独立的变量副本. 接口方法: protected T initialValue( ...

  9. JavaSe:ThreadLocal

    JDK中有一个ThreadLocal类,使用很方便,但是却很容易出现问题.究其原因, 就是对ThreadLocal理解不到位.最近项目中,出现了内存泄漏的问题.其中就有同事在使用ThreadLocal ...

随机推荐

  1. 使用最新版Mybatis逆向工程生成属性不全的问题

    这是出现问题时打印的日志 首先查看表内的字段 这张User表含8个属性,但是在逆向工程过程中生成的Pojo类不全还出现了不存在的属性 主要在逆向工程过程中的一个配置问题,修改generatorConf ...

  2. 用Python帮你实现IP子网计算

    目录 0. 前言 1. ipaddress模块介绍 1.1 IP主机地址 1.2 定义网络 1.3 主机接口 1.4 检查address/network/interface对象 1.4.1 检查IP版 ...

  3. 全文搜索引擎 Elasticsearch

    写在前面 最近在学Elasticsearch , 我相信只要是接触过开发的都会听过Elasticsearch或ELK这么一个技术. 主要用来做全文检索或大数据分析等,之前一直处理了解状态. 所以打算系 ...

  4. python pytesseract——3步识别验证码的识别入门

    验证码识别是个大工程,但入门开始只要3步.需要用到的库PIL.pytesserac,没有的话pip安装.还有一个是tesseract-ocr 下载地址:https://sourceforge.net/ ...

  5. 关于MySQL的经典例题50道

    --1.学生表Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程表 Course(C,Cname,T) - ...

  6. spring-boot-plus XSS跨站脚本攻击处理

    XSS跨站脚本攻击处理 XSS:Cross Site Scripting 跨站脚本攻击(XSS),是目前最普遍的Web应用安全漏洞.这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当 ...

  7. 并发编程之原子操作Atomic&Unsafe

    原子操作:不能被分割(中断)的一个或一系列操作叫原子操作. 原子操作Atomic主要有12个类,4种类型的原子更新方式,原子更新基本类型,原子更新数组,原子更新字段,原子更新引用.Atomic包中的类 ...

  8. CSS隐藏元素 display、visibility、opacity的区别

    关于使指定元素无法在视野内看到,有3个方法 display: none; opacity: 0; visibility: hidden; 1.display: none; 该方法会改变页面布局. 元素 ...

  9. python编程基础之十三

    列表的通用操作: list1 = [43, 65, 76, 6] list2 = [45, 77, 90, 11, 2, 4, 66] print(list1+ list2)  # 列表组合 prin ...

  10. 使用FastReport报表工具生成报表PDF文档

    在我们开发某个系统的时候,客户总会提出一些特定的报表需求,固定的报表格式符合他们的业务处理需要,也贴合他们的工作场景,因此我们尽可能做出符合他们实际需要的报表,这样我们的系统会得到更好的认同感.本篇随 ...