file-type

ThreadLocal机制详解与简单示例

ZIP文件

下载需积分: 50 | 131KB | 更新于2025-01-28 | 163 浏览量 | 2 评论 | 3 下载量 举报 收藏
download 立即下载
ThreadLocal在Java中是一种特殊的变量,用于为不同的线程提供各自的变量副本,使它们能够在各自线程内独立地改变自己的副本,而不会影响其他线程中相同的变量。这种机制为解决多线程中的数据隔离问题提供了一种简便的方式。 ### 知识点一:ThreadLocal的作用和应用场景 ThreadLocal主要用于实现线程安全,特别是在多线程访问共享变量时,防止数据不一致问题。在单线程应用中,可以直接访问成员变量或方法局部变量,而在多线程应用中,由于多线程可以同时访问同一个变量,因此需要考虑同步问题以避免数据竞争和不一致。 使用ThreadLocal可以避免使用synchronized关键字来控制共享变量的访问,从而降低锁的使用,减少线程上下文切换的开销。不过,需要注意的是,ThreadLocal并不适用于解决多线程并发执行方法的共享变量问题。对于这种问题,通常需要使用锁机制或者其他并发工具来确保线程安全。 ### 知识点二:ThreadLocal的内部机制 ThreadLocalMap是ThreadLocal类中的一个静态内部类,它负责存储每一个线程的变量副本。当创建一个ThreadLocal实例时,实际上是创建了该实例的一个特定线程的副本。 每个线程访问ThreadLocal变量时,实际上访问的是自己线程内的ThreadLocalMap中的一个条目(Entry)。ThreadLocalMap使用ThreadLocal对象作为key,以存储的线程局部变量作为value。 ### 知识点三:ThreadLocal的基本使用 ThreadLocal的基本使用方式包括几个步骤:创建ThreadLocal变量、设置变量值、获取变量值和清除变量值。 1. **创建ThreadLocal变量**:通过实例化ThreadLocal类来创建。 2. **设置变量值**:通过调用set()方法将变量值绑定到当前线程。 3. **获取变量值**:通过调用get()方法从当前线程获取变量值。 4. **清除变量值**:通过调用remove()方法来清除当前线程对应的ThreadLocal变量。 ### 知识点四:ThreadLocal的内存泄漏问题 尽管ThreadLocal能够解决线程安全问题,但它可能会导致内存泄漏。这是因为在某些情况下,ThreadLocalMap的生命周期可能会比使用它的线程更长,如果线程结束,而ThreadLocal变量未被清除,就会导致Thread对象与ThreadLocalMap一直被引用,从而无法被垃圾回收器回收,造成内存泄漏。 为了避免内存泄漏,需要注意以下几点: - 使用完毕后,记得调用remove()方法清除当前线程的ThreadLocal变量。 - 在线程池等复用线程的场景下,应该在每次任务结束后清除ThreadLocal变量。 ### 知识点五:ThreadLocalDemo示例解析 假设我们现在要展示一个简单的ThreadLocal使用Demo,可以通过以下代码片段来演示: ```java public class ThreadLocalDemo { // 创建ThreadLocal变量 private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { // 创建并启动线程 Thread thread1 = new Thread(() -> { // 设置变量值 threadLocal.set("Thread1Value"); // 获取并打印变量值 System.out.println("Thread1: " + threadLocal.get()); // 清除变量值 threadLocal.remove(); }); Thread thread2 = new Thread(() -> { // 设置变量值 threadLocal.set("Thread2Value"); // 获取并打印变量值 System.out.println("Thread2: " + threadLocal.get()); // 清除变量值 threadLocal.remove(); }); // 启动线程 thread1.start(); thread2.start(); } } ``` 在这个示例中,我们在主方法中创建了两个线程,每个线程内部设置了ThreadLocal变量,并获取了变量值进行打印,最后在任务执行完毕后清除了ThreadLocal变量。这样可以确保即使在多线程环境下,每个线程中ThreadLocal变量的值都是独立且隔离的,不会相互影响。 ### 总结 ThreadLocal为多线程程序中提供了一种优雅的数据隔离方式,但需要注意合理使用和管理ThreadLocal变量,特别是关注其内存泄漏的问题。通过良好的代码实践和对ThreadLocal机制的理解,可以在很多场景下简化多线程程序的设计,提升程序的健壮性和可维护性。

相关推荐

filetype

我写了一个账号系统,这是他的所有代码,你分析一下功能,看有没有什么问题要改的package com.tplink.nbu.demo.basicspringboot.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface UserAuth { } /* * Copyright (c) 2025, TP-Link. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.aspect; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.Arrays; /** * Description of this file * * @author Liu Long * @version 1.0 * @since 2025/8/21 */ @Aspect @Component @Slf4j public class RequestLogAspect { // 拦截所有controller方法 @Around("execution(public * com.tplink.nbu.demo.basicspringboot.controller.*.*(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes != null ? attributes.getRequest() : null; // 入参打印 if(request != null) { log.info("========================================="); log.info("request URL:{}", request.getRequestURL()); log.info("request method:{}", request.getMethod()); log.info("request ip:{}", request.getRemoteAddr()); log.info("request arguments:{}", Arrays.toString(joinPoint.getArgs())); } // 执行目标方法 Object result = joinPoint.proceed(); // 出参打印 log.info("response result:{}", result); log.info("========================================="); return result; } } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.aspect; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.tplink.nbu.demo.basicspringboot.bean.UserInfo; import com.tplink.nbu.demo.basicspringboot.exception.UnauthorizedException; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Slf4j @Aspect @Component public class UserAuthAspect { @Pointcut("execution(public * com.tplink.nbu.demo.basicspringboot.controller.*.*(..))") public void controllers() { // controller pointcut definition } @Pointcut("@annotation(com.tplink.nbu.demo.basicspringboot.annotation.UserAuth)") public void needUserAuth() { // need user auth pointcut definition } @Around("controllers() && needUserAuth()") public Object around(ProceedingJoinPoint pjp) throws Throwable { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); String credential = this.getCredential(request); if (credential == null) { throw new UnauthorizedException(); } Object[] args = pjp.getArgs(); Object[] newArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { newArgs[i] = this.checkAndAssignUserInfo(args[i], new UserInfo(credential)); } return pjp.proceed(newArgs); } private Object checkAndAssignUserInfo(Object newArg, UserInfo userInfo) { if (newArg instanceof UserInfo) { return userInfo; } return newArg; } private String getCredential(HttpServletRequest request) { return request.getHeader(HttpHeaders.AUTHORIZATION); } } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.bean; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class UserInfo { private String username; private String password; private String email; private String address; public UserInfo(String username){ this.username = username; } } /* * Copyright (c) 2025, TP-Link. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * Description of this file * * @author Liu Long * @version 1.0 * @since 2025/8/21 */ @Data @Component @ConfigurationProperties(prefix = "user.registration") public class UserRegistrationConfig { private int maxUsers; } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.controller; import javax.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.tplink.nbu.demo.basicspringboot.annotation.UserAuth; import com.tplink.nbu.demo.basicspringboot.bean.UserInfo; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginSuccessDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserRegisterDTO; import com.tplink.nbu.demo.basicspringboot.exception.UnauthorizedException; import com.tplink.nbu.demo.basicspringboot.service.UserService; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Slf4j @RestController @RequestMapping("/user") public class UserLogInOutController { @Autowired private UserService userService; @PostMapping("/login") public UserLoginSuccessDTO login(@Valid @RequestBody UserLoginDTO loginDTO) { boolean auth = userService.auth(loginDTO); if (!auth) { throw new UnauthorizedException(); } log.info("{} login", loginDTO.getUsernameOrEmail()); return UserLoginSuccessDTO.builder() .token(loginDTO.getUsernameOrEmail()) .build(); } @UserAuth @PostMapping("/logout") public UserInfo logout(UserInfo userInfo) { log.info("{} logout", userInfo.getUsername()); return userInfo; } @PostMapping("/register") public boolean register(@Valid @RequestBody UserRegisterDTO userRegisterDTO) { boolean success = userService.register(userRegisterDTO); if (!success) { throw new UnauthorizedException(); } log.info("{} register", userRegisterDTO.getUsername()); return true; } } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.dto; import javax.validation.constraints.NotEmpty; import lombok.Getter; import lombok.Setter; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Getter @Setter public class UserLoginDTO { @NotEmpty private String usernameOrEmail; @NotEmpty private String password; } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.dto; import lombok.Builder; import lombok.Getter; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Getter @Builder public class UserLoginSuccessDTO { private String token; } package com.tplink.nbu.demo.basicspringboot.dto; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Email; import lombok.Getter; import lombok.Setter; /** * Description of this file * @author Liu Long * @version 1.0 * @since 2025/8/21 */ @Getter @Setter public class UserRegisterDTO { @NotEmpty private String username; @NotEmpty private String password; @NotEmpty @Email private String email; @NotEmpty private String address; } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @ResponseStatus(value = HttpStatus.UNAUTHORIZED) public class UnauthorizedException extends RuntimeException { } package com.tplink.nbu.demo.basicspringboot.service; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserRegisterDTO; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ public interface UserService { boolean auth(UserLoginDTO loginDTO); boolean register(UserRegisterDTO registerDTO); } /* * Copyright (c) 2020, TP-Link Co.,Ltd. All rights reserved. */ package com.tplink.nbu.demo.basicspringboot.service; import org.springframework.stereotype.Service; import io.micrometer.core.instrument.MeterRegistry; import com.tplink.nbu.demo.basicspringboot.config.UserRegistrationConfig; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserRegisterDTO; import com.tplink.nbu.demo.basicspringboot.bean.UserInfo; import java.util.concurrent.ConcurrentHashMap; import java.util.Map; /** * @author [email protected] * @version 1.0 * @since 2020/7/13 */ @Service public class UserServiceImpl implements UserService { private final MeterRegistry meterRegistry; private final Map<String, UserInfo> userMap = new ConcurrentHashMap<>(); private final UserRegistrationConfig registrationConfig; public UserServiceImpl(MeterRegistry meterRegistry, UserRegistrationConfig registrationConfig) { this.meterRegistry = meterRegistry; this.registrationConfig = registrationConfig; } @Override public boolean auth(UserLoginDTO loginDTO) { String usernameOrEmail = loginDTO.getUsernameOrEmail(); UserInfo user = userMap.get(loginDTO.getUsernameOrEmail()); // 如果没找到用户名,查询是否为邮箱 if (user == null) { for(UserInfo userInfo : userMap.values()) { if (userInfo.getEmail().equals(usernameOrEmail)) { user = userInfo; break; } } } return user != null && loginDTO.getPassword().equals(user.getPassword()); } @Override public boolean register(UserRegisterDTO registerDTO) { if (userMap.size() >= registrationConfig.getMaxUsers()) { throw new IllegalStateException("注册失败:已到达最大用户数" + registrationConfig.getMaxUsers()); } // 用户名是否存在 if (userMap.containsKey(registerDTO.getUsername())) { return false; } // 邮箱是否重复 boolean isTheEmailDuplicated = false; for (UserInfo userInfo : userMap.values()) { if(userInfo.getEmail().equals(registerDTO.getEmail())) { meterRegistry.counter("user.registration.email.duplicate").increment(); isTheEmailDuplicated = true; } } if (isTheEmailDuplicated) { return false; } // 注册新用户信息 UserInfo userInfo = new UserInfo(); userInfo.setUsername(registerDTO.getUsername()); userMap.put(registerDTO.getUsername(), userInfo); return true; } }

资源评论
用户头像
MsingD
2025.07.01
简明扼要地展示了ThreadLocal的基本使用方法,适合初学者入门。👋
用户头像
赵小杏儿
2025.06.12
清晰阐述了ThreadLocalMap与Thread的关联方式,便于理解线程隔离原理。
小凳子腿
  • 粉丝: 87
上传资源 快速赚钱