面试问题整理 持续更新ing

1. Spring声明事务,new一个对象,调用内部方法,还会有事务吗

在Spring框架中,事务管理通常是通过AOP(面向切面编程)代理来实现的。当你在Spring中声明一个事务时,这个事务通常只会应用于通过Spring容器管理的bean的代理对象上的方法调用。

1.Spring声明事务:这通常意味着你在某个类或方法上使用了如@Transactional注解,或者在XML配置中配置了事务管理。
2.new一个对象:当你使用new关键字创建一个对象时,这个对象不是由Spring容器管理的。因此,它不会是一个代理对象,也就不会有Spring的事务管理功能。
3.调用内部方法:由于这个对象不是代理对象,所以即使你在这个对象上调用了一个被@Transactional注解的方法,这个方法也不会在事务的上下文中执行。

但是,有一种情况可能会让你误以为有事务:

如果你的类是一个Spring管理的bean(即它是通过Spring容器创建的),并且你在这个类的内部(而不是通过new)调用了另一个被@Transactional注解的方法,那么这个方法调用通常不会在事务的上下文中执行,除非你使用AopContext.currentProxy()来获取当前bean的代理,并通过这个代理来调用方法。但请注意,这种方法并不推荐,因为它破坏了封装性,并可能导致难以调试的问题。

为了正确地在Spring中管理事务,你应该确保:

1.你的bean是由Spring容器管理的(即它们是通过如@Component、@Service等注解或通过XML配置定义的)。
2.你通过Spring容器(如使用@Autowired注解)来获取这些bean的引用,而不是通过new来创建它们。
3.你在被@Transactional注解的方法上调用其他方法,而不是在类的内部直接调用。

2. 服务器有4g,Jvm设置堆内存2g,堆内存正常,但服务器内存还在减少,这是为什么?

当提到服务器有4G内存,JVM堆内存设置为2G,但服务器内存仍在减少时,这通常意味着除了JVM堆内存之外,还有其他因素在消耗服务器的内存资源。以下是一些可能的原因:

1.非堆内存使用:JVM内存不仅限于堆内存,还有非堆内存区域,如方法区(PermGen/Metaspace在Java 8及以后)、代码缓存、线程栈等。如果JVM启动参数没有正确配置这些非堆内存区域,或者应用程序中创建了过多的线程(导致线程栈占用过多内存),都可能导致服务器内存减少。
2.本地内存使用:应用程序可能使用了本地内存(Native Memory),这部分内存不由JVM管理,但会占用服务器的物理内存。例如,如果应用程序使用了NIO(非阻塞IO)或者JNI(Java Native Interface)等技术,就可能会使用到本地内存。
3.其他进程或服务:服务器上可能运行着其他进程或服务,这些进程或服务也会占用内存。如果它们的内存使用量不断增加,就会导致服务器整体内存减少。
4.内存泄漏:虽然您提到堆内存正常,但仍然存在非堆内存泄漏的可能性。例如,缓存框架(如Guava Cache、EhCache等)如果配置不当,就可能导致内存泄漏。此外,一些第三方库或框架也可能存在内存泄漏的问题。
5.系统缓存:操作系统会使用一部分内存作为缓存(如文件系统缓存、页表缓存等)。当系统需要更多内存时,这些缓存会被回收。因此,在某些情况下,即使应用程序本身没有增加内存使用,服务器的可用内存也可能会减少。

为了确定问题的具体原因,您可以采取以下措施:

1.使用JVM监控工具(如jvisualvm、jconsole等)检查JVM的内存使用情况,包括堆内存和非堆内存的使用情况。
2.使用系统监控工具(如top、free、vmstat等)检查服务器的整体内存使用情况,包括各个进程或服务的内存占用情况。
3.分析应用程序的日志和代码,查看是否存在可能导致内存泄漏或大量使用本地内存的代码段或第三方库。
4.如果可能的话,尝试升级JVM版本或调整JVM启动参数,以优化内存使用。
5.如果怀疑是系统缓存的问题,可以尝试清理系统缓存并观察内存使用情况的变化。但请注意,这可能会影响到系统的性能。

3. 谈谈对jwt的理解

JWT(JSON Web Tokens)是一种开放标准(RFC 7519)的方法,用于在各方之间安全地传输信息。它主要用于身份验证和授权,但也可以用于任何需要传递安全信息的场景。JWT主要由三部分组成:Header(头部)、Payload(负载)和Signature(签名)

1.Header(头部)
JWT的头部通常包含两部分信息:
typ:声明了JWT的类型,这里是JWT。
alg:声明了用于签名JWT的算法,如HS256、RS256或ES256。

这两部分信息会被编码成Base64Url,并作为JWT的第一部分。

2.Payload(负载)
Payload部分包含了JWT的具体信息,这些信息可以包含用户的身份信息、用户权限等。
这些信息也被编码成Base64Url,作为JWT的第二部分。

JWT规范定义了几个标准的声明(或称为"注册声明"),但也可以包含自定义的声明。一些常见的声明包括:
iss(Issuer):发行人
exp(Expiration Time):过期时间
sub(Subject):主题
aud(Audience):受众
nbf(Not Before):在此之前不可用
iat(Issued At):发行时间
jti(JWT ID):JWT的唯一标识符

3.Signature(签名)
Signature部分是对前两部分(Header和Payload)的签名,以确保JWT没有被篡改。

签名的计算方式是:

  • 将Header和Payload进行Base64Url编码后,用.(点)连接在一起。
  • 使用Header中声明的签名算法(如HMAC SHA256或RSA),以及一个密钥(secret或public/private key pair),对连接后的字符串进行签名。
  • 签名后的结果也进行Base64Url编码,并附加到JWT的最后,作为第三部分。

JWT的优点
无状态:JWT不需要在服务器端保存会话信息,因此它非常适合在分布式系统中使用。
可扩展性:JWT可以包含自定义的声明,这使得它可以用于各种用途。
安全性:当使用合适的签名算法和密钥时,JWT可以确保数据在传输过程中的完整性和真实性。
JWT的缺点
令牌大小:由于JWT包含了所有必要的信息,因此它可能会比传统的会话ID更大。
撤销令牌:一旦JWT被签发并发送给客户端,除非过期,否则很难撤销它。这可能需要一些额外的机制,如黑名单。
敏感信息:虽然JWT可以包含任何信息,但通常不建议在JWT中包含敏感信息,因为JWT可以被解码并查看其内容。

4. Jwt中base64加密底层算法

JWT(JSON Web Tokens)中的Base64加密并不是传统意义上的加密,而是一种编码方式。Base64编码的主要目的是将二进制数据转换为一种ASCII字符串格式,这样数据就可以作为文本在HTTP/HTTPS协议中传输,或者在电子邮件等系统中作为文本处理。

Base64编码的底层算法原理如下:

  1. 将给定的字符串(或者二进制数据)转换成对应的字符编码(如UTF-8)。
  2. 将获得的字符编码转换成二进制码。
  3. 对获得的二进制码进行分组操作:
    • 每3个字节(8位二进制)为一组,一共24个二进制位。
    • 将这个24个二进制位分成4组,每个组有6个二进制位,不足6位的,后面补0。
    • 在每个组前面加两个0,这样每个组就又变成了8位,即每个组一个字节,4个组就4个字节了。
  4. 根据Base64的编码表(包含64个字符),用这6位二进制位作为索引,去编码表中查找对应的字符。
  5. 如果原始数据的字节数不是3的倍数,那么最后会剩下1个或2个字节。在这种情况下,会在这些字节后面补0,使其达到3个字节的长度,然后再进行Base64编码。但是,在编码后的字符串的末尾,会用“=”字符来标识填充了多少个字节(1个“=”表示填充了1个字节,2个“=”表示填充了2个字节)。

在JWT中,头部(Header)和负载(Payload)都被单独进行Base64编码,然后与签名(Signature)一起,通过“.”(点)连接成一个完整的JWT字符串。这种编码方式使得JWT可以在文本格式中安全地传输数据,同时也可以在需要时进行解码以恢复原始数据。

需要注意的是,Base64编码并不是一种安全的加密方式,它只是一种编码方式,可以被轻松地解码。因此,JWT的安全性主要依赖于其签名部分,签名部分使用了公钥/私钥对或者共享密钥进行加密,以确保JWT在传输过程中不被篡改。

5. 垃圾回收器算法

垃圾回收器算法是用于自动管理计算机内存中的无用数据(或称为“垃圾”)的一系列策略和技术。这些算法的主要目标是识别不再使用的对象(即垃圾),并释放它们所占用的内存空间,以便程序可以继续有效地运行。

以下是几种常见的垃圾回收器算法:

标记-清除(Mark-Sweep):
标记阶段:从根对象(如全局变量、活动栈等)开始,遍历程序对象的引用链,将可达的对象标记为“存活”。
清除阶段:清除所有未被标记的对象,即释放这些对象占用的内存空间。

标记-整理(Mark-Compact):
与标记-清除算法类似,但在清除阶段后,它会将所有存活的对象移动到内存的一端,从而消除内存碎片。

复制(Copying):
将内存分为两个区域,每次只使用其中一个。在垃圾回收时,将存活的对象复制到另一个区域,然后清空原区域。这种方法适用于年轻代的垃圾回收,因为年轻代中的对象存活率通常较低。

分代收集(Generational Collection):
根据对象的存活时间将内存划分为不同的区域(或称为“代”),如年轻代和老年代。对不同的代采用不同的垃圾回收策略,以提高效率。

引用计数(Reference Counting):
每个对象都有一个引用计数器,每当有一个引用指向该对象时,计数器加1;当引用被删除或超出作用域时,计数器减1。当对象的引用计数为0时,表示该对象不再被引用,可以被回收。但是,这种方法无法解决循环引用的问题。

并行垃圾回收(Parallel Garbage Collection):
使用多个线程同时执行垃圾回收操作,以提高回收效率。适用于多核处理器和多线程应用。

并发垃圾回收(Concurrent Garbage Collection):
在应用程序线程运行的同时执行垃圾回收操作,以减少垃圾回收对程序性能的影响。适用于需要最小化垃圾回收停顿时间的应用场景,如实时应用。

G1(Garbage-First)算法:
一种多代、分代、并发的垃圾回收器,适用于大内存应用。它可以根据性能需求进行配置,并尝试在最小化停顿时间的同时实现高吞吐量。
每种算法都有其特定的优缺点和适用场景,选择合适的算法取决于应用程序的需求和特性。

6. maybatis底层原理

MyBatis的底层原理主要涉及以下几个方面:

  1. 配置文件解析:
  • MyBatis首先会读取配置文件(如XML配置文件),这些配置文件包含了数据库连接信息、Mapper映射文件的位置等关键信息。
  • MyBatis会将配置文件中的信息解析并存储到Configuration对象中,这个对象会在MyBatis的整个生命周期中存在,以便后续重复使用。
  1. SqlSessionFactory构建:
  • 通过SqlSessionFactoryBuilder,使用解析后的Configuration对象,MyBatis会构建SqlSessionFactory实例。SqlSessionFactory是MyBatis的核心组件之一,它充当了应用程序与MyBatis数据库之间的一个交互对象。
  1. SqlSession创建:
  • 通过SqlSessionFactory,应用程序可以创建SqlSession实例。SqlSession是MyBatis的另一个核心组件,它代表了一次与数据库的会话,主要用于执行SQL语句并返回结果。
  1. Mapper映射:
  • Mapper是MyBatis中抽象出来的一个概念,它表示一类DAO(数据访问对象)类的接口。在Mapper接口中,开发者可以定义与数据库操作相关的方法。
  • MyBatis通过XML配置文件或Java注解的方式,将Mapper接口中的方法与SQL语句进行映射。这些SQL语句可以在XML配置文件中编写,也可以直接在Mapper接口的Java注解中编写。
  1. SQL执行:
  • 当应用程序调用Mapper接口中的方法时,MyBatis会根据Mapper映射找到对应的SQL语句。
  • MyBatis会创建一个Executor(执行器)来执行SQL语句。Executor负责将SQL语句发送到数据库,并处理返回的结果。
  • 如果SQL语句执行成功并返回结果,MyBatis会将结果映射为Java对象(通常是Java Bean或自定义的Java类),并返回给应用程序。
  1. 事务管理:
  • MyBatis支持事务管理,可以确保在执行多个SQL语句时,这些语句要么全部成功执行,要么全部不执行(即回滚)。
  • MyBatis通过连接管理器和事务管理器来实现事务管理。连接管理器负责创建和关闭数据库连接,而事务管理器则负责开始、提交或回滚事务。
  1. 缓存处理:
  • MyBatis支持一级缓存(SqlSession级别的缓存)和二级缓存(Mapper级别的缓存)。这些缓存可以提高查询性能,减少数据库访问次数。
  • 一级缓存是默认的,当SqlSession关闭或清空时,一级缓存中的数据会被清除。二级缓存需要手动开启,并且需要配置缓存策略(如LRU、FIFO等)。

总结起来,MyBatis的底层原理主要是通过解析配置文件、构建SqlSessionFactory、创建SqlSession、Mapper映射、SQL执行、事务管理和缓存处理等一系列步骤,实现应用程序与数据库之间的交互。

7. 谈谈spring的两大特性

Spring框架作为Java企业版(Java EE)的轻量级替代品,具有许多引人注目的特性。其中,两大核心特性通常被认为是依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)

依赖注入(Dependency Injection, DI)
依赖注入是Spring框架的一个核心功能,它帮助开发者通过外部化配置的方式,自动地管理组件之间的依赖关系。在Spring中,对象之间的依赖关系不是通过硬编码来设定的,而是通过配置来完成的。这使得代码更加清晰、解耦,并提高了可测试性和可维护性。

Spring支持三种主要的依赖注入方式:

  • 构造器注入:在创建对象时,通过构造器的参数传递依赖的对象。
  • Setter注入:通过调用对象的setter方法来注入依赖的对象。
  • 字段注入:直接在类的字段上使用@Autowired注解来实现依赖注入。

面向切面编程(Aspect-Oriented Programming, AOP)
面向切面编程是一种编程范式,用于处理系统中分布于多个模块的横切关注点(cross-cutting concerns),如日志记录、事务管理、安全性等。在Spring中,AOP是通过动态代理来实现的,这使得开发者能够在不修改原有代码的情况下,为系统添加新的功能或行为。

Spring AOP的核心概念包括:
切面(Aspect):定义了横切关注点的行为,通常包含了一个或多个通知(Advice)和切点(Pointcut)的定义。
通知(Advice):描述了切面在何时以及如何应用其增强功能到目标方法上。通知有多种类型,如前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)等。
切点(Pointcut):用于定义哪些方法应该被切面增强。它可以通过表达式来匹配方法名、参数、返回类型等。
连接点(Joinpoint):程序执行过程中的一个点,如方法的调用或异常的处理。通知就是在这个点上被应用的。
织入(Weaving):将切面应用到目标对象上,从而创建出代理对象的过程。这个过程可以在编译时、类加载时或运行时完成。
通过AOP,开发者可以将横切关注点从业务逻辑中分离出来,使得代码更加清晰、易于维护和复用。同时,AOP也使得开发者能够更加灵活地控制横切关注点的行为,如动态地开启或关闭日志记录、调整事务的传播行为等。

8. Spring自动装配逻辑

Spring自动装配的完成流程主要包括以下几个步骤:

启动应用:
当Spring Boot应用启动时,它会首先加载主类(通常是带有@SpringBootApplication注解的类),这个注解包含了@EnableAutoConfiguration,用于启用自动装配功能。
加载META-INF/spring.factories
Spring Boot会扫描类路径(包括所有jar包)下的META-INF/spring.factories文件。这个文件包含了Spring Boot启动时需要加载的自动配置类。
解析自动配置类
对于spring.factories文件中定义的每个自动配置类,Spring Boot会尝试加载并解析这些类。这些类通常使用@Configuration注解,并通过@Bean注解定义了一组bean。
条件装配
自动配置类中的bean并不是都会被创建和注册到Spring容器中。Spring Boot使用条件注解(如@ConditionalOnClass、@ConditionalOnBean等)来决定是否应该创建和注册某个bean。这些条件注解可以基于类路径中是否存在某个类、是否已经定义了某个bean等条件进行判断。
创建和注册bean
对于满足条件的bean,Spring Boot会创建这些bean的实例,并将它们注册到Spring容器中。这个过程通常是通过Java配置类中的@Bean方法实现的。
依赖注入
一旦bean被创建并注册到Spring容器中,Spring就会根据bean之间的依赖关系进行依赖注入。这通常是通过在字段、构造器参数或方法参数上使用@Autowired注解实现的。
完成装配
当所有的bean都被创建、注册并依赖注入完成后,Spring的自动装配过程就完成了。此时,应用程序就可以使用这些bean来执行相应的业务逻辑了。

需要注意的是,Spring的自动装配过程是一个动态的过程,它会在应用程序启动时自动完成。同时,由于Spring Boot使用了条件装配的机制,因此可以通过自定义自动配置类和使用条件注解来灵活地控制bean的创建和注册过程。

9. synchronized与lock区别

synchronized和Lock在Java中都是用于多线程同步的工具,但它们在使用方式和特性上存在一些区别。

使用方式

  • synchronized是Java中的一个关键字,它可以修饰方法或代码块。当修饰方法时,锁的对象是当前实例对象(对于非静态方法)或类的Class字节码对象(对于静态方法)。当修饰代码块时,锁的对象是synchronized后面括号里配置的对象。
  • Lock是一个接口,它提供了更多的同步特性,比如可以中断等待的线程、尝试获取锁等。Lock接口的实现类包括ReentrantLock等。使用Lock时,需要显式地调用lock()方法来获取锁,并在finally块中调用unlock()方法来释放锁。

特性:

  • synchronized具有原子性和可见性。原子性指的是在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行。可见性是指一个线程对共享变量进行了修改,另一个线程可以立即读取得到修改后的最新值。
  • synchronized是可重入锁,即一个线程可以多次执行synchronized,重复获取同一把锁。它的内部锁对象中会有一个计数器记录线程获取几次锁,在执行完同步代码块时,计数器的数量会-1,直到计数器的数量为0,就释放这个锁。但synchronized是不可中断锁,即一个线程获得锁后,另一个线程想要获得锁,必须处于阻塞或等待状态,如果第一个线程不释放锁,第二个线程会一直阻塞或等待,不可被中断。
  • Lock接口提供了更灵活的锁机制。例如,Lock接口的实现类ReentrantLock是可重入锁,也是可中断锁。此外,Lock还提供了tryLock()方法,该方法可以在指定时间范围内尝试获取锁,如果时间到了还没有获取到锁,就选择放弃。Lock还提供了Condition接口,可以实现线程间的通信。

总的来说,synchronized和Lock都是用于多线程同步的工具,但synchronized使用简单,而Lock提供了更丰富的功能和更灵活的同步机制

10. synchronized锁升级

synchronized锁升级是Java为了提高锁的效率而引入的一种机制。在JDK 1.6及以后的版本中,synchronized增加了锁的升级这样一个机制来平衡数据安全和性能的关系。具体来说,synchronized锁升级的过程可以分为以下几个阶段:

无锁:在没有线程竞争锁的情况下,对象处于无锁状态。此时,没有任何线程持有该锁,也没有线程正在执行同步代码块或同步方法。
偏向锁:当一段同步代码长时间被同一个线程访问时,该线程会自动进入偏向锁状态。在偏向锁状态下,当线程再次访问同步代码时,不需要做任何的检查,直接执行,从而降低了获取锁的代价。偏向锁的“偏”指的是锁会偏向于第一个获取它的线程。
轻量级锁(自旋锁):当锁竞争逐渐激烈时,偏向锁会升级为轻量级锁。在轻量级锁状态下,一个或多个线程通过CAS(Compare and Set)操作去争抢锁。如果CAS操作成功,则线程获取了轻量级锁并继续执行同步块;如果CAS操作失败,说明存在竞争,虚拟机会通过自旋(spinning)等待其他线程释放锁。轻量级锁的目的是减少线程挂起的开销,提高并发性能。
重量级锁:如果自旋等待不成功,轻量级锁会升级为重量级锁。在重量级锁状态下,虚拟机会将线程阻塞,并使用操作系统的互斥量来实现锁的释放和获取。重量级锁的开销相对较大,因为它涉及到用户态和内核态的切换以及线程挂起和唤醒等操作。

锁的升级过程是从低到高逐渐进行的,无锁→偏向锁→轻量级锁→重量级锁。锁的升级是不可逆的,即一旦升级为重量级锁,就不能再降级为轻量级锁或偏向锁。通过这种方式,synchronized可以根据锁的竞争情况动态调整锁的级别,从而提高程序的并发性能。

11. 拦截器与过滤器的区别

拦截器(Interceptor)和过滤器(Filter)在多个方面存在显著的区别,主要包括以下几个方面:

实现原理

  • 过滤器是基于函数回调的。
  • 拦截器则是基于Java的反射机制(动态代理)实现的。

使用范围

  • 过滤器(Filter)是Servlet规范的一部分,实现的是javax.servlet.Filter接口,其使用通常依赖于Tomcat等容器,因此过滤器主要在web程序中使用。
  • 拦截器(Interceptor)是一个Spring组件,并由Spring容器管理,并不依赖于Tomcat等容器,因此它可以单独使用。

触发时机

  • 过滤器(Filter)在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
  • 拦截器(Interceptor)可以在请求到达目标处理器之前、处理器处理请求之后以及视图渲染之前执行特定的操作。

拦截的请求范围

  • 过滤器(Filter)在执行过程中会执行两次,分别在请求和响应阶段。
  • 拦截器(Interceptor)通常只执行一次,围绕目标处理器(如Controller)的执行而执行。

功能

  • 过滤器(Filter)主要用于对请求进行统一处理,如字符编码、安全验证等。
  • 拦截器(Interceptor)则更为灵活,可以用于实现如权限控制、日志记录、数据校验、缓存处理等更为复杂的功能。

配置

  • 过滤器的配置需要在web.xml文件中进行,或者通过注解方式配置在Servlet 3.0及以后的版本中。
  • 拦截器的配置则通常在Spring配置文件中进行,或者使用注解方式配置在Spring MVC中。

执行顺序

  • 过滤器(Filter)在拦截器(Interceptor)之前执行,即请求首先通过过滤器,然后进入拦截器,最后到达目标处理器。

12. 多线程如何保证数据一致性

在多线程环境中,保证数据一致性是一个重要的问题。以下是几种常用的方法来确保数据一致性:

使用synchronized关键字
synchronized关键字可以用于方法或代码块,它确保同一时间只有一个线程可以执行被synchronized保护的代码段。这可以防止多个线程同时修改共享数据,从而保证数据一致性。
使用Lock接口
Java的java.util.concurrent.locks包提供了Lock接口,它提供了比synchronized更灵活的锁机制。使用ReentrantLock等实现类,可以显式地控制锁的获取和释放,以及设置锁的公平性。
使用Atomic类
Java的java.util.concurrent.atomic包提供了一组原子类,如AtomicInteger、AtomicLong等。这些类提供了原子性的操作,可以在多线程环境中安全地修改数值类型的变量。
使用volatile关键字
volatile关键字可以确保变量的可见性,即当一个线程修改了某个变量的值,其他线程能立即看到这个修改。但请注意,volatile并不能保证复合操作的原子性。
使用线程安全的集合类
Java的并发包(java.util.concurrent)提供了一组线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等。这些集合类内部已经实现了线程安全,可以直接在多线程环境中使用。
避免共享可变状态
尽可能避免在多个线程之间共享可变状态。如果可能的话,每个线程应该有自己的工作数据和副本,这样可以减少线程之间的冲突和数据不一致的风险。
使用事务
在数据库操作中,可以使用事务来确保数据一致性。事务可以确保一组操作要么全部成功,要么全部失败,从而避免数据不一致的情况。
使用分布式锁
在分布式系统中,可以使用分布式锁来确保数据一致性。分布式锁允许多个进程或线程在分布式环境中协调对共享资源的访问。
使用消息队列
消息队列(如RabbitMQ、Kafka等)可以用于解耦系统组件,并确保数据的顺序性和一致性。通过将消息发送到队列,并在消费者端进行处理,可以避免多个线程同时修改共享数据的问题。
设计合理的并发模型
根据具体的应用场景和需求,设计合理的并发模型。例如,可以使用生产者-消费者模型、读者-写者模型等来处理并发问题。

13. 谈谈threadlocalMap

ThreadLocalMap 是 ThreadLocal 的内部类,是 ThreadLocal 的核心实现部分。它允许我们为每个线程创建独立的变量,这些变量在不同线程之间不会相互影响。

以下是关于 ThreadLocalMap 的详细解释:

数据结构

  • ThreadLocalMap 实际上是一个基于哈希表的 key-value 数据结构。
  • 它使用 Entry 数组来存储键值对,其中 Entry 继承了 WeakReference(弱引用),但需要注意的是,只有键(key)是弱引用,值(value)不是。
  • 当发生哈希冲突时,ThreadLocalMap 使用线性探测法来解决。

与 ThreadLocal 的关系

  • ThreadLocal 是一个泛型类,用于在线程中定义局部变量。
  • ThreadLocal 本身并不存储值,它只是作为一个键(key)在线程的 ThreadLocalMap 变量中,用于让线程从 ThreadLocalMap 获取值(value)。
  • 每个值都是与特定的 ThreadLocal 关联在一起的。
  • 不同的线程有不同的 ThreadLocalMap 对象变量,因此它们可以存储不同的值,而不会相互干扰。

使用方法

  • 创建一个 ThreadLocal 实例,例如:ThreadLocal threadLocal = new ThreadLocal<>();
  • 使用 set 方法设置本地变量的值,例如:threadLocal.set(“value”);
  • 使用 get 方法获取本地变量的值,例如:String value = threadLocal.get();
  • 当不再需要本地变量时,使用 remove 方法进行移除,例如:threadLocal.remove();

内部实现原理

  • ThreadLocalMap 使用线性探测的散列表实现,用 Entry 数组来存储键值对。
  • 当发生哈希冲突时,ThreadLocalMap 会逐个往后寻找空槽或者匹配的键。
  • 由于键是弱引用,当没有强引用指向键时,它可能会被垃圾回收器回收,但对应的值仍会留在 ThreadLocalMap 中,这可能会导致内存泄漏。因此,在使用 ThreadLocal 时,建议在使用完毕后及时调用 remove 方法来清除不再需要的键值对。

总的来说,ThreadLocalMap 为 ThreadLocal 提供了线程间的隔离性,使得每个线程都可以拥有自己的独立变量。同时,它也需要注意内存泄漏的问题,并在适当的时候进行清理。

14. 权限校验工具机制

在权限管理和身份验证领域,确实存在多种工具和机制,每种都有其优缺点和适用场景。JWT(JSON Web Token)作为一种常用的身份验证和授权机制,具有其独特的优势,如轻量级、自包含和易于跨域使用等。然而,就像任何技术一样,JWT也存在一些安全挑战。

针对JWT的安全性问题,一些更安全的权限管理工具和技术可以考虑:

OAuth 2.0:OAuth 2.0 是一个用于授权的行业标准协议,而不是一个完整的认证协议。它允许第三方应用获取有限的访问权限,而无需获取用户的用户名和密码。OAuth 2.0 可以与 JWT 一起使用,以提供更安全的身份验证和授权机制。通过OAuth 2.0,第三方应用可以获得一个短期的访问令牌(access token),该令牌具有特定的作用域和有效期,从而限制对资源的访问。
OpenID Connect:OpenID Connect 是基于 OAuth 2.0 的一个身份验证层。它定义了一种身份层,允许客户端验证用户的身份,并获得有关该用户的基本信息(如姓名、电子邮件地址等)。OpenID Connect 提供了更丰富的身份验证和授权功能,包括用户身份信息的获取和验证。
SAML (Security Assertion Markup Language):SAML 是一种用于交换身份验证和授权数据的 XML 标准。与 JWT 相比,SAML 提供了更复杂的身份验证和授权模型,支持更广泛的身份验证方法(如 X.509 证书、Kerberos 票据等)。然而,SAML 的实现可能更加复杂,并且不如 JWT 那样轻量级和易于使用。
自定义权限管理系统:根据特定应用的需求,可以构建自定义的权限管理系统。这种系统可以根据应用的业务逻辑和安全性要求来定制身份验证和授权机制。自定义系统可以提供更精细的权限控制、更灵活的策略配置以及更强大的审计和监控功能。然而,构建和维护自定义系统可能需要更多的开发资源和专业知识。

15. fegin丢失token如何解决

Feign在调用时丢失token的问题可以通过多种方法来解决。以下是一些可能的解决方案:

全局配置
如果你的项目中使用了HttpClient或RestTemplate等调用接口,你可以在调用之前申请Token,并将其添加到请求头中。确保在每次Feign请求之前,Token都被正确地添加到请求头。
Bean注入
你可以通过Bean的方式将Token注入到Feign的客户端配置中。这样,每次Feign发起请求时,都会自动使用这个Token。
实现RequestInterceptor
Feign支持自定义拦截器(RequestInterceptor),你可以在拦截器中获取Token并将其添加到请求的头部。这样,每次Feign发起请求时,都会经过这个拦截器,从而确保Token被正确地添加到请求头。
使用ThreadLocal
在多线程环境下,你可以使用ThreadLocal来存储Token。在请求开始之前,将Token放入ThreadLocal中,然后在拦截器或请求处理逻辑中从ThreadLocal中获取Token。需要注意的是,在使用完ThreadLocal后,要及时清理其中的数据,避免内存泄漏。
异步调用处理
如果你在使用CompletableFuture等异步工具类进行服务间调用时遇到了token失效的问题,可以尝试将Feign调用其他服务的操作置于异步处理之前。这样,你只需要进行一次Feign调用,就可以避免在异步处理中因为多次调用导致的token失效问题。
检查Feign配置
确保你的Feign配置正确无误。例如,检查是否启用了Hystrix(如果使用了的话),以及Hystrix的配置是否正确。有时候,配置问题也可能导致Token丢失。
日志和调试
开启Feign的日志功能,并检查日志输出以获取更多关于请求和响应的信息。

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值