2021年7月29日这几天是我离阿里最近的时候,因为收到了阿里本部的面试机会,技术面3轮 最后还有一个hr面.
我也发现了几个问题:
首先: 日常工作中没有准备过自己的简历
其次: 日常并没有准备面试题以及没有深入了解Java中常用框架的源码和原理等
最后: 查看网上一些人分析一面的面试题,我9成都不能回答出来,
自我感觉: 简历很难看,阿里还是挺友好的,还是给了我一次面试的机会,最终结果一面也没有过去.
加油吧!
给自己几个告诫:
-
记得要经常稳固自己用的东西和深入理解并记录笔记!
-
经常修改自己的简历,在不同招聘网站中看看自己的价值!
-
不要给自己太大压力,健康最重要!有钱没钱都要过下去.
-
为了自己喜欢的东西努力吧!
我始终有一个梦: 那就是步入大厂!
什么时候开始都不晚,此刻就是你开始的起点!
加油吧!
ThreadLocal
官方注释:
此类提供线程局部变量。这些变量不同于它们的普通对应变量,因为每个访问一个(通过其get或set方法)的线程都有自己的、独立初始化的变量副本。ThreadLocal实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户 ID 或事务 ID)。例如,下面的类生成每个线程本地的唯一标识符。线程的 id 在第一次调用ThreadId.get()时被分配,并且在后续调用中保持不变。
import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); }
}
只要线程处于活动状态并且可以访问ThreadLocal实例,每个线程都持有对其线程局部变量副本的隐式引用;线程消失后,它的所有线程本地实例副本都将进行垃圾回收(除非存在对这些副本的其他引用)。
几个问题
官方注释和使用中的几个问题:
-
ThreadLocal是什么?
-
ThreadLocal在一个任务中多个线程间传递相同的一个数据,如何编程使用?
-
多个任务间同一组业务代码,ThreadLocal如何保证数据的安全性(原理)?
-
线程池中使用ThreadLocal如何防止内存泄漏?
-
应用场景都有哪些?
-
面试让你说一下ThreadLocal?
1. ThreadLocal是什么?
ThreadLocal 位置处于 java.lang下,是泛型类(public class ThreadLocal<T> )
ThreadLocal 是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据
2. ThreadLocal 编程案例
案例背景: 用户登录后生成token,前端调用所有的接口会将token放置在请求头中.后端要对用户操作和接口间的权限,获取用户信息等处理,在过滤器中统一处理token后将用户信息缓存起来,以便程序可以直接获取到当前操作用户.
业务流向结构说明:
案例运行情况:
具体代码:
/**
* 用户~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 10:29
*/
@Data
@AllArgsConstructor
public class TLUser {
/**
* 用户ID
*/
private Integer userId;
/**
* 用户姓名
*/
private String name;
/**
* 用户角色 : user / admin
* user 就是普通用户
* admin 就是管理员,可以进行发货
*/
private String role;
}
/**
* 用户权限过滤~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 10:59
*/
public class TLUserPermissionsFilter {
public static void filter(String url) {
TLUser loginTLUser = ThreadLocalUserUtil.getLoginTLUser();
if ("/goods".equals(url)) {
// 查看商品都可以通过
}
if ("/order".equals(url)) {
// 订单 必须 登录
if (null == loginTLUser) {
throw new RuntimeException("下订单之前必须登录");
}
}
if ("/delivery".equals(url)) {
// 查看商品都可以通过
if (!loginTLUser.getRole().equals("admin")) {
throw new RuntimeException("当前账号没有权限,不能发货");
}
}
}
}
/**
* token 校验 过滤器~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 10:57
*/
public class TokenEqualsFilter {
public static void filter(String token, String url) {
// token 校验判断
if (null == token || "".equals(token)) {
if (!"/goods".equals(url)) {
throw new RuntimeException("token is null or ''");
}
}
ThreadLocalUserUtil.loginUser(token);
}
}
/**
* 用于本地缓存用户~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 10:36
*/
public class ThreadLocalUserUtil {
public static ThreadLocal<TLUser> userThreadLocal = new ThreadLocal<>();
/**
* 用来模拟 获取用户的操作
*
* @param token 用户token
*/
public static void loginUser(String token) {
// 模拟 当前用户是 普通用户
if ("user".equals(token)) {
userThreadLocal.set(new TLUser(1, "张三", "user"));
}
// 模拟 当前用户是 管理员
if ("admin".equals(token)) {
userThreadLocal.set(new TLUser(2, "小丽", "admin"));
}
// 模拟 游客
if (null == token || "".equals(token)) {
userThreadLocal.set(new TLUser(0, "游客", "游客"));
}
}
public static TLUser getLoginTLUser() {
return userThreadLocal.get();
}
public static void removeLoginTLUser() {
userThreadLocal.remove();
}
}
/**
* 业务处理类~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 11:06
*/
public class TLBiz {
public static void queryGoofs() {
if (ThreadLocalUserUtil.getLoginTLUser() == null) {
System.out.println("游客查询");
}
System.out.println(ThreadLocalUserUtil.getLoginTLUser().getName() + "查询goods");
}
public static void order() {
System.out.println(ThreadLocalUserUtil.getLoginTLUser().getName() + "下订单");
}
public static void delivery() {
System.out.println(ThreadLocalUserUtil.getLoginTLUser().getName() + "发货");
}
}
/**
* threadlocal测试类~
*
* @author <发哥讲Java-694204477@qq.com>
* @version 1.0
* @date 2021-07-29 11:05
*/
public class TestThreadlocalMain {
public static void main(String[] args) {
// 调用接口
// String url = "/goods";
// String url = "/order";
String url = "/delivery";
String token = "user";
// String token = "admin";
// String token = null;
// 经过过滤器
TokenEqualsFilter.filter(token, url);
TLUserPermissionsFilter.filter(url);
// 操作业务类
// TLBiz.queryGoofs();
// TLBiz.order();
TLBiz.delivery();
// 防止内存泄漏
ThreadLocalUserUtil.removeLoginTLUser();
}
}
3. ThreadLocal原理
ThreadLocal底层是通过ThreadLocalMap来实现的.(ThreadLocalMap是ThreadLocal的静态内部类,Thread里面有一个变量类型是ThreadLocalMap)
每个thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
4. 内存泄漏
如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把设置的key,value,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决办法是,在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清除Eentry对象.
5. 应用场景
-
Javaweb中操作用户的缓存
-
连接管理(一个线程持有一个连接,该连接对象可以在不同方法之间进行传递,线程之间不共享同一个连接)
6. 说一下ThreadLocal?
1->5读上一遍,回答就可以了.
公众号:发哥讲
这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
代码分享