尚硅谷java基础测试

尚硅谷java基础测试String 与 String intern 这段代码在 JDK 6 中运行 会得到两个 false 而在 JDK 7 中运行 会得到一个 true 和一个 false 产 生差异的原因是 在 JDK 6 中 intern 方法会把首次遇到的字符串实例复制到永久代的字符串常量池 中存储 返回的也是永久代里面这个字符串实例的引用 而由 StringBuilde 创建的字符串对象实例在 Java 堆上

大家好,我是讯享网,很高兴认识大家。



String与String.intern()

这段代码在JDK 6中运行,会得到两个false,而在JDK 7中运行,会得到一个true和一个false。产 生差异的原因是,在JDK 6中,intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池 中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在 Java堆上,所以必然不可能是同一个引用,结果将返回false。

而JDK 7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false,这是因为“java” 这个字符串在执行StringBuilder.toString()之前就已经出现过了(在加载sun.misc.Version这个类的时候进入常量池的),字符串常量池中已经有它的引用,不符合intern()方法要求“首次遇到”的原则,“计算机软件”这个字符串则是首次 出现的,因此结果返回true。

AQS

可重入锁

可重复递归调用的锁,在外层获得锁之后,在内层无需等待锁的释放,可以直接使用,并且不会发生死锁

隐式锁synchronized
  • 原理
    每个锁对象拥有一个锁计数器和一个指向持有该锁线程的指针。
    当执行monitorenter时,如果目标锁对象的计数器为0,说明该锁没有被其他对象所持有,JVM会将该锁对象的持有线程设置成当前线程,并将其计数器加1。
    在目标锁计数器不为0的情况下,如果锁对象的持有线程是当前线程,那么JVM将其计数器加1,否则需要等待,直至持有锁线程释放锁。
    当执行monitorexit时,JVM则需要将锁对象的计数器减1。计数器为0代表锁已释放。
显示锁ReentrantLock

LockSupport

3种线程等待唤醒机制
  • Object中的wait() / notify() / notifyAll()
  • 不能脱离synchronized代码块使用,否则会抛出IllegalMonitorStateException异常
  • 先wait后notify、notifyAll,等待中的线程才能被唤醒,顺序不能改变
  • Condition的await() / siginal() / siginalAll()
  • 必须配合lock()方法使用,否则抛出IllegalMonitorStateException异常
  • 等待唤醒调用顺序不能改变
  • LockSupport提供park()和unpark()方法实现阻塞和唤醒线程的过程
  • LockSupport和每一个使用它的线程之间有一个许可(permit)进行关联,permit有0和1两种状态,默认为0,即无许可证状态
  • 调用一次unpark方法,permit加1变成1。每次调用park方法都会检查许可证状态,如果为1,则消耗掉permit(1 -> 0)并立刻返回;如果为0,则进入阻塞状态。permit最多只有一个,重复调用unpark也不会累积permit。

AQS源码分析

AQS:用来构建锁或其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态。如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁的分配。主要通过CLH队列的变体实现,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS抽象的表现。它将请求共享资源的线程封装成队列的节点(Node),通过CAS自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的控制效果。

CLH队列:Craig、Landin and Hagersten队列,是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。

尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_三级缓存

AQS相关类:ReentrantLock、CountDownLatch、CyclicBarrier、ReentrantReadWriteLock、Semaphore

尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_尚硅谷第二阶段java笔试考试题_02

通过ReentrantLock类进行源码分析

以非公平锁为例:

lock()

讯享网

acquire()

 

tryAcquire(arg)

讯享网

addWaiter(Node.EXCLUSIVE)

 

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

 

unlock()

 

unparkSuccessor()

 

Spring

AOP的顺序

通知类型

@Before:前置通知,目标方法之前执行

@After:后置通知,目标方法之后执行(始终执行)

@AfterReturning:返回通知,执行方法结束前执行(异常不执行)

@AfterThrowing:异常通知,出现异常时执行

@Around:环绕通知,环绕目标方法执行

SpirngBoot1和SpringBoot2对aop通知执行顺序的影响

Spring4.x

  • 正常执行:@Around > @Before > 方法打印 > @Around > @After > @AfterReturning
  • 抛出异常:@Around > @Before > @After > @AfterThrowing > 异常信息

Spring5.x

  • 正常执行:@Around > @Before > 方法打印> @AfterReturning > @After > @Around
  • 抛出异常:@Around > @Before > @AfterThrowing > @After > 异常信息

循环依赖

多个bean之间相互依赖,形成了一个闭环。Spring循环依赖抛出BeanCurrentlyInCreationException。

只要注入方式是setter且是单例的,就不会有循环依赖问题。

Spring三级缓存

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取的都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

第一级(单例池):singletonObjects,存放已经经历了完整生命周期的bean对象。

第二级:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充)。

第三级:Map<String, ObjectFactory<?>> singletonFactories,存放可以生成Bean的工厂。

尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_面试_03

源码解析

尚硅谷java基础测试尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_字符串_04

尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_面试_05

实例化:内存中申请一块空间

初始化:属性填充,完成属性的各种赋值


第一层缓存singletonObjects存放已经初始化好的Bean;第二层缓存earlySingletonObjects存放实例化但未初始化的Bean;第三层缓存singletonFactories存放的是FactoryBean,如果某个类实现了FactoryBean,那么依赖注入的不是该类,而是该类对应的Bean。


A/B两个互相依赖的对象在三级缓存中迁移说明?

  1. A创建过程中需要用到B,于是A将自己放到三级缓存里面,去实例化B
  2. B实例化的时候需要A,于是B先查一级缓存,没有,再查二级缓存,还没有,再查三级缓存,找到A并把三级缓存中的A放到二级缓存里面,并删除三级缓存里面的A
  3. B顺利初始化完毕,将自己放到一级缓存中(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存中拿到B,完成A的创建并把A放到一级缓存中

Spring解决循环依赖依靠的是Bean“中间态”的概念,即已经实例化但是还未初始化的状态。实例化过程是通过构造器实现的,如果A还未创建好则不能提前暴露,所以构造器注入无法解决循环依赖问题。


尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_java_06

Spring解决循环依赖过程:

  1. 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
  2. 在getSingleton()方法中,从【一级缓存】中查找,没有,返回null
  3. doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
  4. 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法
  5. 进入AbstractAutowireCapableBeanFactory#doCreateBean,先利用反射创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中
  6. 对beanA进行属性填充(populateBean),此时检测到beanA依赖于beanB,于是开始查找beanB
  7. 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则先在【三级缓存】中创建,然后给beanB填充属性
  8. 此时 beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从【三级缓存】中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA
  9. 将beanA从【三级缓存】移动到【二级缓存】,beanB就获取到了beanA的依赖,beanB顺利完成实例化,从【三级缓存】直接移动到【一级缓存】
  10. 随后beanA继续属性填充工作,从【一级缓存】中获取到beanB,beanA完成初始化,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

Redis

string list hash set zset bitmap hyperloglog geo

命令关键字不区分大小写,key区分大小写

五大数据类型的落地应用

String
  • 设置获取:set key value / get key
  • 批量设置获取:mset key value[key1 value1…] / mget key [key1]
  • 递增:incr key / incrby key increment
  • 递减:decr key / decrby key increment
  • 获取字符串长度:strlen key
  • 分布式锁:
    setnx key value
    set key value [EX seconds] [PX milliseconds] [NX|XX]
  • 参数说明:
    EX – key在多少秒后过期
    PX – key在多少毫秒之后过期
    NX – 当key不存在的时候创建key,效果等同于setnx
    XX – 当key存在时候,覆盖key
  • 应用场景:商品编号、订单号、点赞、踩:incr item:001
Hash

Map<String, Map<Object, Object>>

  • 设置获取:hset key field value / hset key field
  • 批量设置获取:hmset key field value [field1 value1…] / hmset key field [field1]
  • 获取所有字段值:hgetall key
  • 获取key中字段的数量:hlen key
  • 删除key:hdel key
  • 应用场景:
    购物车早期,中小厂可用
    新增商品:hset shopcar:1024 11111 1
    新增商品:hset shopcar:1024 22222 1
    增加商品商量:hincrby shopcar:1024 22222 1
    全部选择:hgetall shopcar:1024
List
  • 向列表左边/右边添加元素:lpush/rpush key value [value…]
  • 查看列表:lrange key start stop
  • 获取列表中元素个数:llen key
  • 应用场景:订阅的公众号推送文章
Set
  • 添加元素:sadd key member[member1…]
  • 删除元素:srem key member[member1]
  • 获取集合中所有元素:smembers key
  • 判断元素是否在集合中:sismember key member
  • 获取集合中元素个数:scard key
  • 从集合中随机弹出一个元素,元素不删除:srandmember key [弹出元素个数]
  • 从集合中随机弹出一个元素,元素删除:spop key [弹出元素个数]
  • 集合运算:
  • 差集:sdiff key [key1]
  • 交集:sinter key [key1]
  • 并集:sunion key [key1]
  • 应用场景:
  • 微信抽奖小程序:参与抽奖(sadd)、显示参与人数(scard)、抽奖(srandmember、spop)
  • 微信朋友圈点赞:点赞(sadd 朋友圈id 点赞用户id1 点赞用户id2)、取消点赞(srem 朋友圈id 点赞用户id1)、展现所有点赞用户(smember 朋友圈id)、点赞数(scard 朋友圈id)
  • 微博好友关注社交关系:共同关注的人(sinter k1 k2)、我关注的人也关注他(sismember k1 v、sismember k2 v 都返回true)
  • 推送可能认识的人:A认识B不认识的推送给B(sdiff A B)
Zset

向有序集合中加入一个元素和该元素的分数。

  • 添加元素:zadd key score member [score1 member1…]
  • 按照分数从小到大顺序返回索引在start到stop之间的元素:zrange key start stop [WITHSCORES]
  • 获取元素的分数:zscore key member
  • 删除元素:zrem key member [member1]
  • 获取指定分数范围的元素:zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
  • 增加某个元素的分数:zincrby key increment member
  • 获取集合中元素的数量:zcard key
  • 获取指定分数范围内元素个数:zcount key min max
  • 按照排名范围删除元素:zremrangebyrank key start stop
  • 获取元素的排名:
  • 从小到大:zrank key member
  • 从大到小:zrevrank key member
  • 应用场景:
  • 根据商品销售量对商品排序展示
  • 抖音热搜

分布式锁

 

内存淘汰策略

redis默认内存大小:如果不设置最大内存大小或者最大内存大小为0,在64位操作系统下不限制内存大小,32位操作系统下最多使用3GB内存。默认单位是字节,配置时要注意单位转换。

生产上内存大小最好设置成最大物理内存的四分之三。

设置内存大小方式:

  • 修改配置文件 maxmemory
  • 通过命令修改
 

查看redis内存情况:info memory

过期键删除策略:定时删除(键过期立即删除)、惰性删除(使用键时判断是否过期,过期则删除)、定期删除(抽样删除过期键)

内存淘汰策略:noevication(默认)、allkeys-lru、volatile-lru、allkeys-random、volatile-random、allkeys-lfu、volatile-lfu、volatile-ttl

lru算法

lru算法思想:查找快、插入快、删除快、且要有先后排序

lru算法核心:哈希链表LinkedHashMap,本质是HashMap+DoubleLinkedList,时间复杂度是O(1)

案例一:

 

案例二:

 

尚硅谷第二阶段java笔试考试题 尚硅谷笔试答案_java_07

小讯
上一篇 2025-01-02 16:09
下一篇 2024-12-24 21:21

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/4673.html