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. 字符串转数字(with Java)

    1. 字符串中提取数字 两个函数可以帮助我们从字符串中提取数字(整型.浮点型.字符型...). parseInt().parseFloat() valueOf() String str = " ...

  2. 基于Spark的电影推荐系统(实战简介)

    写在前面 一直不知道这个专栏该如何开始写,思来想去,还是暂时把自己对这个项目的一些想法 和大家分享 的形式来展现.有什么问题,欢迎大家一起留言讨论. 这个项目的源代码是在https://github. ...

  3. 使用Storm进行词频统计

    词频统计 1.需求:读取指定目录的数据,并且实现单词计数功能 2.实现方案: Spout用于读取指定文件夹(目录),读取文件,将文件的每一行发射到Bolt SplitBolt用于接收Spout发射过来 ...

  4. Spring boot 官网学习笔记 - Spring Boot CLI 入门案例

    安装CLI https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.1.RELEASE/spring-b ...

  5. jenkins+svn+Ant+tomcat+非maven项目构建

    首先,输入项目名称,创建一个自由风格的项目; 然后,配置旧项目的策略参数,目的是防止构建项目太多,占用资源. 下一步,jdk版本选择: 下一步,关联svn项目. 下一步:配置ant 看不清,再来一张. ...

  6. 短视频处理LanSoEditor-SDK之功能介绍

    短视频处理LanSoEditor-SDK之功能介绍  (注释: 我们的SDK每3周更新一次, 一下功能是在2.8.2版本上列出的,可能不是最新的功能, 请知悉) 和别家最大的不同在于: 别人提供功能, ...

  7. 【产品】PM常用的流程图

    一.流程图分类 UML有很多种,大体可以分类两类:行为型的图和结构型的图.平时工作中的流程图,只要能把事情清晰的表明,用何种流程图表现形式,其实都无所谓. 但是,作为一名产品经理,共有哪些种类的流程图 ...

  8. 检测MySQL主从是否异常

    #!bin/bash user='root' passwd="123" host="192.168.192.156" mycmd="mysql -u$ ...

  9. 从零开始入门 K8s | 应用存储和持久化数据卷:存储快照与拓扑调度

    作者 | 至天 阿里巴巴高级研发工程师 一.基本知识 存储快照产生背景 在使用存储时,为了提高数据操作的容错性,我们通常有需要对线上数据进行 snapshot ,以及能快速 restore 的能力.另 ...

  10. Map集合(双列集合)

    Map集合(双列集合)Map集合是键值对集合. 它的元素是由两个值组成的,元素的格式是:key=value. Map集合形式:{key1=value1 , key2=value2 , key3=val ...