当前位置: 首页 > wzjs >正文

自己电脑做网站访问快吗百度百科分类方法

自己电脑做网站访问快吗,百度百科分类方法,做汽车团购网站,还能用的wap网站目录 1. ThreadLocal基本概念 1.1 核心原理 1.2 主要特性 2. ThreadLocal API详解 2.1 核心方法 2.2 基本使用方式 3. ThreadLocal使用场景与实战 3.1 场景一:用户身份信息传递 实现步骤 1.创建用户上下文类 2.创建过滤器或拦截器来设置和清理用户信息 …

目录

1. ThreadLocal基本概念

1.1 核心原理

1.2 主要特性

2. ThreadLocal API详解

2.1 核心方法

2.2 基本使用方式

3. ThreadLocal使用场景与实战

3.1 场景一:用户身份信息传递

实现步骤

1.创建用户上下文类

2.创建过滤器或拦截器来设置和清理用户信息 

3.业务逻辑中使用 

 4.总结

3.2 场景二:事务管理

实现步骤

1.创建数据库连接管理类

 2.创建事务管理工具类

 3.在业务代码中使用

4.总结

3.3 场景三:简化日期格式化

实现步骤

1.创建日期工具类

2. 在多线程环境中使用

3.4 场景四:追踪请求链路

实现步骤

1.创建追踪上下文类

 2.创建请求拦截器

3.配置拦截器 

 4.创建日志工具类(可自定义)

 5.创建RestTemplate拦截器传递追踪ID

 6.配置RestTemplate

7. 在业务代码中使用

3.5 场景五:slf4j——MDC全局日志打印

实现步骤

1.配置lombok(日志约束xml文件)

2.创建拦截器

3.配置拦截器

4.呈现效果

3.6 场景六:线程安全的数据缓存

实现步骤

 1.创建本地缓存类

 2.使用例子

4. ThreadLocal实现继承性 - InheritableThreadLocal

4.1 基本用法

4.2 用于跨线程上下文传递

实现步骤

 1.创建上下文类

2.创建异步执行器 

3.在多线程环境中使用 

4.Controller引入 

5. ThreadLocal常见问题与最佳实践

5.1 内存泄漏问题

最佳实践

1.总是调用remove方法

2. 使用try-with-resources模式

5.2 父子线程值传递问题

使用TransmittableThreadLocal

 1.依赖配置

2.基本实践 

3.Spring集成 

5.3 ThreadLocal的值修改

最佳实践

5.4 初始化ThreadLocal值

6. 总结


1. ThreadLocal基本概念

ThreadLocal是Java提供的一个线程本地变量工具,它允许我们创建只能被同一个线程读写的变量。从线程的角度看,就好像是线程在访问自己的私有变量一样,避免了共享变量可能带来的并发问题。

在这里强调一点,ThreadLocal是绝对不会应用于线程同步的场景的!

ThreadLocal本质:隔离线程数据!而非共享数据!

这一点本文会有意无意多次强调!所以大家在学习TheadLocal的时候,要摒弃掉JUC那一套理论!大伙儿只要清楚,ThreadLocal就是多线程对于共享变量的独立备份,至于这个共享变量是否同时只能被一个线程修改,这一点是无关紧要的。

因为每个线程栈的上下文都是独立、私有的,所以Threadlocal修饰的变量存储的值相对来说肯定也是独立、私有的。

1.1 核心原理

ThreadLocal的工作原理可以简单概括为:

  • 每个Thread对象内部维护了一个ThreadLocalMap
  • ThreadLocalMap是一个定制化的哈希表,其中key是ThreadLocal对象的弱引用,value是该线程存储的变量副本
  • 当线程访问ThreadLocal变量时,实际是在访问自己的ThreadLocalMap中对应的条目

1.2 主要特性

  • 线程隔离性:每个线程拥有各自独立的变量副本
  • 减少同步:避免多线程环境下的同步操作
  • 方便传递上下文:无需通过参数传递上下文信息
  • 资源清理:需要手动移除不再使用的ThreadLocal变量

2. ThreadLocal API详解

2.1 核心方法

方法描述
T get()获取当前线程的ThreadLocal变量副本
void set(T value)设置当前线程的ThreadLocal变量副本
void remove()移除当前线程的ThreadLocal变量副本
protected T initialValue()返回ThreadLocal变量的初始值(默认返回null)
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)Java 8引入,创建带初始值的ThreadLocal

这里列出了5个,但是只要记住前三个就行:获取值、设置值、移除值(防止内存泄漏

2.2 基本使用方式

// 创建ThreadLocal变量
private ThreadLocal<String> threadLocal = new ThreadLocal<>();// 设置值
threadLocal.set("线程特定的值");// 获取值
String value = threadLocal.get();// 使用完后移除值
threadLocal.remove();

3. ThreadLocal使用场景与实战

3.1 场景一:用户身份信息传递

在Web应用中,用户身份信息需要在同一个请求的不同组件、不同层之间传递,使用ThreadLocal可以避免在多个方法间传递用户对象。

实现步骤
1.创建用户上下文类
public class UserContext {private static final ThreadLocal<User> USER_THREAD_LOCAL = new ThreadLocal<>();public static void setUser(User user) {USER_THREAD_LOCAL.set(user);}public static User getUser() {return USER_THREAD_LOCAL.get();}public static void clear() {USER_THREAD_LOCAL.remove();}
}
2.创建过滤器或拦截器来设置和清理用户信息 
    @Component
    public class UserContextFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;try {// 从请求中获取用户信息(例如从JWT token中)User user = extractUserFromRequest(req);// 设置到ThreadLocal中UserContext.setUser(user);// 继续处理请求chain.doFilter(request, response);} finally {// 请求结束后清理ThreadLocal,避免内存泄漏UserContext.clear();}}private User extractUserFromRequest(HttpServletRequest request) {// 从请求头或Cookie中提取用户信息并创建User对象// 当然请求头和Cookie不一定非要带上完整的User信息,只要给一个能唯一锁定User的key就好// 这里省略具体实现...return new User();}
    }
    
    3.业务逻辑中使用 
      @Service
      public class UserService {public void processUserRequest() {// 直接获取当前线程关联的用户信息User currentUser = UserContext.getUser();// 使用用户信息进行业务处理if (currentUser != null && currentUser.hasPermission("SOME_ACTION")) {// 执行需要权限的操作} else {throw new UnauthorizedException("No permission");}}
      }
      
       4.总结

      实战中我们可以经常遇到这样的场景:请求实时获取当前发起请求用户个人信息

      其实就可以依据上述步骤来完成。

      不可否认信息来源肯定还是来源于:token、header这类载体。常规处理方式,可能在解析到用户信息后放入缓存,或者实时获取。

      但是这种处理方式是基于线程维度来处理,也就是说,在整个线程的生命周期中,所有上下文能够用到用户信息的地方都只需要经过一次解析后,随时随地从内存的ThreadLocalMap中拿到。

      3.2 场景二:事务管理

      在需要手动管理事务的场景中,可以使用ThreadLocal存储数据库连接。

      实现步骤
      1.创建数据库连接管理类
      public class ConnectionManager {private static final ThreadLocal<Connection> CONN_HOLDER = new ThreadLocal<>();public static Connection getConnection() throws SQLException {Connection conn = CONN_HOLDER.get();if (conn == null) {// 创建新连接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");// 设置为手动提交conn.setAutoCommit(false);// 存储到ThreadLocalCONN_HOLDER.set(conn);}return conn;}public static void beginTransaction() throws SQLException {Connection conn = getConnection();if (conn.getAutoCommit()) {conn.setAutoCommit(false);}}public static void commitTransaction() throws SQLException {Connection conn = CONN_HOLDER.get();if (conn != null) {conn.commit();}}public static void rollbackTransaction() throws SQLException {Connection conn = CONN_HOLDER.get();if (conn != null) {conn.rollback();}}public static void close() {Connection conn = CONN_HOLDER.get();if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}CONN_HOLDER.remove();}}
      }
      
       2.创建事务管理工具类
      public class TransactionManager {public static void executeInTransaction(TransactionCallback callback) {try {// 开始事务ConnectionManager.beginTransaction();// 执行业务逻辑callback.execute();// 提交事务ConnectionManager.commitTransaction();} catch (Exception e) {// 发生异常时回滚try {ConnectionManager.rollbackTransaction();} catch (SQLException ex) {ex.printStackTrace();}throw new RuntimeException(e);} finally {// 关闭连接ConnectionManager.close();}}// 回调接口public interface TransactionCallback {void execute() throws Exception;}
      }
      
       3.在业务代码中使用
      public class UserRepository {public void createUserWithAddress(User user, Address address) {TransactionManager.executeInTransaction(() -> {// 使用同一个连接执行多个SQLConnection conn = ConnectionManager.getConnection();// 插入用户try (PreparedStatement ps = conn.prepareStatement("INSERT INTO users (username, email) VALUES (?, ?)",Statement.RETURN_GENERATED_KEYS)) {ps.setString(1, user.getUsername());ps.setString(2, user.getEmail());ps.executeUpdate();// 获取生成的用户IDtry (ResultSet rs = ps.getGeneratedKeys()) {if (rs.next()) {int userId = rs.getInt(1);// 插入地址,关联用户IDtry (PreparedStatement addrPs = conn.prepareStatement("INSERT INTO addresses (user_id, street, city, country) VALUES (?, ?, ?, ?)")) {addrPs.setInt(1, userId);addrPs.setString(2, address.getStreet());addrPs.setString(3, address.getCity());addrPs.setString(4, address.getCountry());addrPs.executeUpdate();}}}}});}
      }
      
      4.总结

      这种手动处理db链接的方式,其实也是借用了jdbc底层源码的套路,只不过在应用层做了一层拦截,强制让一个事务的sql群走一个链接。避免了手动另开事务进程,保证操作原子性。

      能够有效避免数据库链接被占满的异常情况。

      3.3 场景三:简化日期格式化

      SimpleDateFormat不是线程安全的,因为它的内部状态(如 Calendar 实例)会在多线程环境下被修改,导致数据错乱、异常或错误结果。

      使用ThreadLocal可以为每个线程提供独立的实例。

      实现步骤
      1.创建日期工具类
      public class DateUtil {// ThreadLocal存储每个线程的SimpleDateFormat实例private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));// 日期转字符串public static String formatDate(Date date) {return DATE_FORMATTER.get().format(date);}// 字符串转日期public static Date parseDate(String dateStr) throws ParseException {return DATE_FORMATTER.get().parse(dateStr);}// 修改日期格式public static void setDateFormat(String pattern) {DATE_FORMATTER.set(new SimpleDateFormat(pattern));}
      }
      
      2. 在多线程环境中使用
        public class DateFormatExample {public static void main(String[] args) {// 创建线程池ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 20; i++) {final int taskId = i;executor.submit(() -> {try {// 每个线程设置不同的日期格式if (taskId % 2 == 0) {DateUtil.setDateFormat("yyyy-MM-dd");} else {DateUtil.setDateFormat("MM/dd/yyyy HH:mm");}// 使用当前线程的格式化器Date now = new Date();String formattedDate = DateUtil.formatDate(now);System.out.println("Thread " + Thread.currentThread().getId() + " formatted date: " + formattedDate);// 解析日期Date parsedDate = DateUtil.parseDate(formattedDate);System.out.println("Thread " + Thread.currentThread().getId() + " parsed date: " + parsedDate);} catch (ParseException e) {e.printStackTrace();}});}executor.shutdown();}
        }
        

        3.4 场景四:追踪请求链路

        在微服务架构中,需要追踪一个请求在不同服务间的调用链路,可以使用ThreadLocal来存储和传递追踪ID。

        实现步骤
        1.创建追踪上下文类
        public class TraceContext {private static final ThreadLocal<String> TRACE_ID_HOLDER = new ThreadLocal<>();public static void setTraceId(String traceId) {TRACE_ID_HOLDER.set(traceId);}public static String getTraceId() {String traceId = TRACE_ID_HOLDER.get();// 如果不存在,则生成新的追踪IDif (traceId == null) {traceId = generateTraceId();TRACE_ID_HOLDER.set(traceId);}return traceId;}public static void clear() {TRACE_ID_HOLDER.remove();}// 生成唯一的追踪IDprivate static String generateTraceId() {return UUID.randomUUID().toString().replace("-", "");}
        }
        
         2.创建请求拦截器
        @Component
        public class TraceInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(TraceInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 尝试从请求头中获取追踪IDString traceId = request.getHeader("X-Trace-ID");// 如果没有,则生成新的if (traceId == null || traceId.isEmpty()) {traceId = TraceContext.getTraceId();} else {TraceContext.setTraceId(traceId);}logger.info("Processing request with trace ID: {}", traceId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 请求完成后清理TraceContext.clear();}
        }
        
        3.配置拦截器 
          @Configuration
          public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TraceInterceptor traceInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(traceInterceptor);}
          }
          
           4.创建日志工具类(可自定义)

          这一步根据自己项目实际的需求来,这里只是简单举例,比如封装一个切面统一打印日志。

          public class LogUtil {private static final Logger logger = LoggerFactory.getLogger(LogUtil.class);public static void info(String message) {logger.info("[TraceID: {}] {}", TraceContext.getTraceId(), message);}public static void error(String message, Throwable throwable) {logger.error("[TraceID: {}] {}", TraceContext.getTraceId(), message, throwable);}// 其他日志级别方法...
          }
          
           5.创建RestTemplate拦截器传递追踪ID
          @Component
          public class TraceRestTemplateInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {// 获取当前线程的追踪ID,并添加到请求头request.getHeaders().add("X-Trace-ID", TraceContext.getTraceId());return execution.execute(request, body);}
          }
          
           6.配置RestTemplate
          @Configuration
          public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate(TraceRestTemplateInterceptor traceInterceptor) {RestTemplate restTemplate = new RestTemplate();// 添加追踪拦截器restTemplate.setInterceptors(Collections.singletonList(traceInterceptor));return restTemplate;}
          }
          
          7. 在业务代码中使用
          @Service
          public class UserService {@Autowiredprivate RestTemplate restTemplate;public User getUserDetails(String userId) {LogUtil.info("Fetching user details for user: " + userId);// 调用用户服务User user = restTemplate.getForObject("/api/users/{id}", User.class, userId);LogUtil.info("Retrieved user: " + user.getUsername());// 调用订单服务List<Order> orders = restTemplate.getForObject("/api/orders?userId={id}", List.class, userId);LogUtil.info("Retrieved " + orders.size() + " orders for user");// 继续处理...return user;}
          }
          

          3.5 场景五:slf4j——MDC全局日志打印

          MDC是slf4j提供的线程链路追踪的一种优雅实现,其实和上面那种方式差不多,只不过这里是作用于运行日志。

          因为slf4j是日志门面框架,无论你的项目是使用logback还是log4j2来作为日志框架,最终的日志形式肯定会基于一个xml文件来约束。

          MDC可以保存日志上下文,在打印日志时,打印出来这些上下文信息以上面那个3.4为基础,我们也可以在xml里配置每次都把traceId这个信息打印出来。

          实现步骤
          1.配置lombok(日志约束xml文件)
          <configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 确保包含 %X{traceId} --><pattern>[%thread] [traceId=%X{traceId}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="STDOUT" /></root>
          </configuration>
          2.创建拦截器
          public class HeaderInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 从 Header 中提取信息String authToken = request.getHeader("Authorization");String traceId = request.getHeader("X-Trace-Id");// 存储到 ThreadLocalif (authToken != null) {RequestContext.setAuthToken(authToken);}if (traceId != null) {RequestContext.setTraceId(traceId);MDC.put("traceId", traceId); 这里加上值}return true; // 继续执行后续拦截器和控制器}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 请求完成后清理 ThreadLocal,避免内存泄漏RequestContext.clear();}
          }
          3.配置拦截器
          @Configuration
          public class WebConfig implements WebMvcConfigurer {@Autowiredprivate HeaderInterceptor traceInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(traceInterceptor);}
          }
          
          4.呈现效果
          [http-nio-8081-exec-2] [traceId=123e4567-e89b-12d3-a456-426614174000] INFO  org.example.service.WebService - get token:Basic dXNlcm5hbWU6cGFzc3dvcmQ=,traceId:123e4567-e89b-12d3-a456-426614174000
          

          3.6 场景六:线程安全的数据缓存

          在不同线程需要缓存不同数据的场景下,使用ThreadLocal可以避免同步问题。

          其实这种场景很少,文章开头也说了ThreadLocal一般是不会用于线程同步的场景中,但是不代表ThreadLocal不能做。

          线程同步的场景,一般都是使用异步编排、信号量、JUC工具类来解决的。

          实现步骤
           1.创建本地缓存类
          public class LocalCache<K, V> {private final ThreadLocal<Map<K, V>> cache = ThreadLocal.withInitial(HashMap::new);public V get(K key) {return cache.get().get(key);}public void put(K key, V value) {cache.get().put(key, value);}public void remove(K key) {cache.get().remove(key);}public boolean containsKey(K key) {return cache.get().containsKey(key);}public void clear() {cache.get().clear();}// 完全清除ThreadLocalpublic void removeThreadLocal() {cache.remove();}
          }
          
           2.使用例子
          public class ProductService {// 创建产品缓存private final LocalCache<String, Product> productCache = new LocalCache<>();public Product getProduct(String productId) {// 首先尝试从缓存获取Product product = productCache.get(productId);if (product == null) {// 缓存中不存在,从数据库加载product = loadProductFromDb(productId);// 放入缓存productCache.put(productId, product);}return product;}private Product loadProductFromDb(String productId) {// 从数据库加载产品(示例逻辑)try {// 模拟数据库访问延迟Thread.sleep(100);return new Product(productId, "Product " + productId, 99.99);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("Loading product interrupted", e);}}// 在一个批处理任务结束时清理缓存public void cleanupCache() {productCache.removeThreadLocal();}
          }
          

          4. ThreadLocal实现继承性 - InheritableThreadLocal

          每个线程栈都是独立私有的,普通的ThreadLocal无法将变量值从父线程传递到子线程,为解决这个问题,Java提供了InheritableThreadLocal。

          4.1 基本用法

          public class InheritableThreadLocalExample {// 创建InheritableThreadLocal变量private static final InheritableThreadLocal<String> CONTEXT = new InheritableThreadLocal<>();public static void main(String[] args) {// 在主线程中设置值CONTEXT.set("Main thread value");System.out.println("Main thread: " + CONTEXT.get());// 创建子线程Thread childThread = new Thread(() -> {// 子线程可以继承父线程的值System.out.println("Child thread: " + CONTEXT.get());// 子线程修改值不会影响父线程CONTEXT.set("Child thread value");System.out.println("Child thread after update: " + CONTEXT.get());});childThread.start();try {childThread.join();} catch (InterruptedException e) {e.printStackTrace();}// 主线程的值不受子线程修改的影响System.out.println("Main thread after child execution: " + CONTEXT.get());}
          }
          

          4.2 用于跨线程上下文传递

          在复杂的异步调用场景中,使用InheritableThreadLocal传递上下文信息。

          实现步骤
           1.创建上下文类
          public class ApplicationContext {private static final InheritableThreadLocal<Map<String, Object>> CONTEXT = new InheritableThreadLocal<Map<String, Object>>() {@Overrideprotected Map<String, Object> initialValue() {return new HashMap<>();}};public static void set(String key, Object value) {CONTEXT.get().put(key, value);}@SuppressWarnings("unchecked")public static <T> T get(String key) {return (T) CONTEXT.get().get(key);}public static void remove(String key) {CONTEXT.get().remove(key);}public static void clear() {CONTEXT.get().clear();}public static void removeThreadLocal() {CONTEXT.remove();}
          }
          
          2.创建异步执行器 
          @Configuration
          public class AsyncConfig {@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.setThreadNamePrefix("AsyncTask-");// 创建自定义的TaskDecorator,用于传递ThreadLocalexecutor.setTaskDecorator(task -> {// 获取当前线程的上下文Map<String, Object> context = new HashMap<>(ApplicationContext.getAll());return () -> {// 在任务执行前,将上下文设置到新线程try {context.forEach(ApplicationContext::set);// 执行原始任务task.run();} finally {// 任务执行完毕,清理上下文ApplicationContext.clear();}};});return executor;}
          }
          
          3.在多线程环境中使用 
          @Service
          public class AsyncService {@Async("taskExecutor")public CompletableFuture<String> processAsync(String input) {// 获取从调用线程继承的上下文String userId = ApplicationContext.get("userId");String traceId = ApplicationContext.get("traceId");System.out.println("Processing in async thread - UserId: " + userId + ", TraceId: " + traceId);// 处理业务逻辑try {Thread.sleep(1000); // 模拟耗时操作return CompletableFuture.completedFuture("Processed: " + input);} catch (InterruptedException e) {Thread.currentThread().interrupt();return CompletableFuture.failedFuture(e);}}
          }
          
          4.Controller引入 
          @RestController
          @RequestMapping("/api")
          public class AsyncController {@Autowiredprivate AsyncService asyncService;@GetMapping("/process")public CompletableFuture<String> process(@RequestParam String input, @RequestHeader("X-User-Id") String userId) {// 设置上下文信息ApplicationContext.set("userId", userId);ApplicationContext.set("traceId", UUID.randomUUID().toString());try {// 调用异步服务return asyncService.processAsync(input);} finally {// 清理主线程的上下文ApplicationContext.clear();}}
          }
          

          5. ThreadLocal常见问题与最佳实践

          5.1 内存泄漏问题

          ThreadLocal使用不当可能导致内存泄漏,因为ThreadLocalMap的Key是ThreadLocal的弱引用,而Value是强引用。

          最佳实践
          1.总是调用remove方法

          在使用完ThreadLocal后调用remove()方法 

          try {threadLocal.set(value);// 使用ThreadLocal变量的代码
          } finally {threadLocal.remove();
          }
          
          2. 使用try-with-resources模式

          创建AutoCloseable的ThreadLocal包装类

          public class AutoCloseableThreadLocal<T> implements AutoCloseable {private final ThreadLocal<T> threadLocal = new ThreadLocal<>();public void set(T value) {threadLocal.set(value);}public T get() {return threadLocal.get();}@Overridepublic void close() {threadLocal.remove();}
          }// 使用方式
          try (AutoCloseableThreadLocal<String> local = new AutoCloseableThreadLocal<>()) {local.set("value");// 使用local变量
          } // 自动调用close方法,清理ThreadLocal
          

          5.2 父子线程值传递问题

          如前所述,普通ThreadLocal无法将值从父线程传递到子线程。使用InheritableThreadLocal可解决这个问题,但它也有局限性:子线程只在创建时继承父线程的值,后续父线程的修改不会影响子线程。

          对于线程池场景,可以使用阿里巴巴开源的TransmittableThreadLocal库。

          使用TransmittableThreadLocal
           1.依赖配置
          <dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.14.2</version>
          </dependency>
          
          2.基本实践 
          import com.alibaba.ttl.TransmittableThreadLocal;
          import com.alibaba.ttl.TtlRunnable;public class TtlExample {private static final TransmittableThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>();public static void main(String[] args) {// 创建线程池ExecutorService executor = Executors.newFixedThreadPool(2);// 设置主线程的值CONTEXT.set("Initial Value");// 提交任务executor.submit(TtlRunnable.get(() -> {System.out.println("Task 1: " + CONTEXT.get()); // 输出: Initial Value}));// 修改值CONTEXT.set("Updated Value");// 提交另一个任务executor.submit(TtlRunnable.get(() -> {System.out.println("Task 2: " + CONTEXT.get()); // 输出: Updated Value}));executor.shutdown();}
          }
          
          3.Spring集成 
          @Configuration
          public class ThreadPoolConfig {@Beanpublic Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(100);executor.setThreadNamePrefix("Async-");// 包装Executor,使其支持TransmittableThreadLocalexecutor.setTaskDecorator(runnable -> TtlRunnable.get(runnable));return executor;}
          }
          

          5.3 ThreadLocal的值修改

          ThreadLocal存储的对象如果是可变的,其内部状态可能被意外修改,导致线程间的数据污染。

          最佳实践
          1. 存储不可变对象:尽量存储String、Integer等不可变对象
          2. 使用深拷贝:如果必须存储可变对象,在get和set时进行深拷贝
          public class SafeUserContextHolder {private static final ThreadLocal<User> USER_HOLDER = new ThreadLocal<>();// 存储用户时进行深拷贝public static void setUser(User user) {if (user == null) {USER_HOLDER.remove();} else {// 创建用户对象的副本User userCopy = new User();userCopy.setId(user.getId());userCopy.setUsername(user.getUsername());userCopy.setRoles(new ArrayList<>(user.getRoles()));USER_HOLDER.set(userCopy);}}// 获取用户时进行深拷贝public static User getUser() {User user = USER_HOLDER.get();if (user == null) {return null;}// 创建用户对象的副本User userCopy = new User();userCopy.setId(user.getId());userCopy.setUsername(user.getUsername());userCopy.setRoles(new ArrayList<>(user.getRoles()));return userCopy;}public static void clear() {USER_HOLDER.remove();}
          }
          

          5.4 初始化ThreadLocal值

          可以通过覆盖initialValue()方法或使用withInitial()方法设置初始值。

          // 方法1:覆盖initialValue方法
          private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");}};// 方法2:使用withInitial方法(Java 8+)
          private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
          

          6. 总结

          ThreadLocal是一个强大的工具,适用于需要在同一线程内共享数据但又不希望进行线程同步的场景。它的主要应用场景包括:

          1. 用户身份信息传递:在Web应用中传递用户上下文
          2. 事务管理:存储和传递数据库连接
          3. 线程安全的对象:为每个线程提供专用的非线程安全对象
          4. 请求链路追踪:在分布式系统中追踪请求

          注意事项:

          • 及时清理:使用完毕后调用remove()方法,避免内存泄漏
          • 合理封装:将ThreadLocal的操作封装在统一的上下文管理类中
          • 注意线程复用:在线程池环境中要特别小心ThreadLocal的使用
          • 数据隔离清楚认识ThreadLocal是为了隔离线程数据,而非共享数据
          http://www.dtcms.com/wzjs/181318.html

          相关文章:

        1. 怎么做网站测试云南最新消息
        2. 工信部网站备案查询 验证码杭州网站优化平台
        3. 仿做静态网站多少钱河源疫情最新通报
        4. 微信小程序第三方免费制作平台东莞网站优化关键词排名
        5. 杭工e家app是哪个网站做的惠州seo优化服务
        6. 网站的空间域名济南网站建设哪家专业
        7. 网站建设在作用是什么意思企业网站建设规划
        8. 网站建设规范布局有几部分武汉seo价格
        9. 网站开发google手机怎么创建网站
        10. 重庆所有做网站的公司有哪些免费发布外链
        11. dw做的网站怎么在vs关键词调词平台费用
        12. 东莞政府网站建设百度seo搜索排名
        13. 做目录的网站爱站网长尾挖掘工具
        14. 搜索企业的软件哪个好游戏优化
        15. 网站底部的图标中文搜索引擎排行榜
        16. 网站建立连接不安全怎么处理竞价 推广
        17. 安卓app开发常州网站建设优化
        18. seo关键词排名优化提升百度免费seo
        19. 江苏建设工程集团深圳seo优化排名公司
        20. 招聘网站建设人员要求网站seo优化课程
        21. 郑州电商小程序定制青岛seo经理
        22. 阿里云快速备份网站十大网络推广公司排名
        23. 建设网站一般要多钱大学生网页设计主题
        24. 做网站开发用什么软件长沙搜索排名优化公司
        25. 做独立网站需要注意些什么手续外贸营销网站制作
        26. 2017做哪些网站能致富十大软件培训机构
        27. 网店起名大全免费取名windows优化大师怎么卸载
        28. 网站改版方案流程关系营销案例
        29. 摄影的网站设计特点微信app小程序开发
        30. wordpress系统语言设置搜索引擎推广和优化方案