《天净沙·我·二面知识点总结》 双非菜鸡奇葩,面试项目框架,java java,卑微学子去哪?


别问 问就是为了面试豁出了老命

布隆过滤器

布隆过滤器主要是针对大量数据的一个预判,可以给出两种结果,1.一定不存在 2. 可能存在的
布隆过滤器使用多个映射的hash函数将一个数据直接映射到一个bit数组上,当进行插入的时候,n个映射函数的值会直接到达布隆过滤器的索引位置,并且设置树脂为1,因此如果发生hash碰撞会发生,某个数组的位置多次为1,情况,因此会得出可能存在的结论,但是如果查找的时候,n个hash函数有任意一位过滤的数组值为不为1,那么则一定不存在。

秒杀系统中为什么会产生超卖的现象?

由于使用mysql的时候数据库本身的存储引擎innodb是改操作是排他锁,但是读的操作是不上锁的,当到达最后一个的时候,如果都查到还剩下一个,那么其它的请求过来都会同时的更改数据库,那么就会造成超卖,也是快照读的一个弊病。

怎么解决超卖现象?

最简单的思路,就是每次在sql语句中,更改的时候,在where的时候设条件大于0,但是这样子一旦超过数据库的承受能力,还是会超读
每次的读操作都上排他锁,这样子可以避免这个现象,但是会导致性能大幅度降低
使用redis是最好的方法,可以把要秒杀的内容放到redis的队列中,使用一次则删除一次

细致深入:Redis实现秒杀系统

核心redis的操作命令,sextnx(细粒度上锁) + expire(强制锁释放)

  1. 首先需要将秒杀的数据放到一个redis中,然后在操作redis的时候可以用setnx去操作数据,所谓细粒度锁是一个抽象概念,sexnx就是具体实现(其实更加直白的理解一些,普通的get/put是一个原子操作,但是一系列操作就不再是原子性的了,setnx是一个原子操作,所以可以作为一个细粒度锁)
  2. 更加直接的来,就是将数据都加入到redis的list中,然后使用Lpop命令不断获取就ok(移除并返回列表的第一个元素)

Redis深入

redis能用的的加锁命令分表是INCR、SETNX、SET

redis所有的单独的命令都是原子的

INCR

这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一。然后其它用户在执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中。

SETNX

这种加锁的思路是,如果 key 不存在,将 key 设置为 value如果 key 已存在,则 SETNX 不做任何动作

SET

借助 Expire 来设置就不是原子性操作了。所以还可以通过事务来确保原子性,但是还是有些问题,所以官方就引用了另外一个,使用 SET 命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能

上锁可能出现的问题

1、 redis发现锁失败了要怎么办?中断请求还是循环请求?

可以利用循环进行重复请求

2、 循环请求的话,如果有一个获取了锁,其它的在去获取锁的时候,是不是容易发生抢锁的可能?

循环的时候可以sleep一下

3、 锁提前过期后,客户端A还没执行完,然后客户端B获取到了锁,这时候客户端A执行完了,会不会在删锁的时候把B的锁给删掉?

不一定,可以借助key-value的value进行判断

锁续约问题

Redission
当成功的获取一个锁的时候,就会产生一个 watch dog 进行锁续期,每10秒去检查一次,然后重置成设定的过期时间
加锁机制是lua脚本
解锁过程就比较简单了,如果是程序计数器则继续进行-1,直到等于0,然后删除该key即可(unlock操作)

Lua原子性

Redis 使用单个 Lua 解释器去运行所有脚本,并且, Redis 也保证脚本会以原子性(atomic)的方式执行: 当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。 这和使用 MULTI / EXEC 包围的事务很类似。 在其他别的客户端看来,脚本的效果(effect)要么是不可见的(not visible),要么就是已完成的(already completed)

了解java内存模型吗,JVM整体和并发的一些关系

Java内存模型就是定义程序中变量(静态变量、数组对象元素等,不包括局部变量、方法参数)的访问规则

内存模型操作的八个指令

博客原文

  • lock:作用于主内存变量,将该变量标识为一个线程独占的状态

  • unlock:作用于主内存变量,将独占状态释放

  • read:作用于主内存变量,将值拷贝到工作内存中

  • load:作用于工作内存中的变量,将值放到工作内存中的变量副本中

  • use:作用于工作内存中的变量,将值传给执行引擎

  • asign:作用于工作内存中的变量,将执行引擎中的值赋给工作内存中的变量

  • store:作用于工作内存中的变量,将值传给主内存

  • write:作用于主内存中的变量,将工作内存中返回的值放到主内存变量中

    volatile

    保证load与use必须相邻调用,即要use这个变量,必定先执行read/load,这样每次都能获取到最新的变量值;它又保证asign与store必须相邻调用,即在工作内存中将该变量改了之后,必定会先同步到主内存中

AQS

阻塞队列

是一个双向的链表,概念上的队列,但不是真正的实现也是队列

条件队列

是根据condition创建出来的队列,上锁后可以负责对线程的监视,比synchronized的监视器更加灵活,是一个单向的链表,当唤醒界节点的时候会直接添加到阻塞队列中

Node节点

1. waitstatus

  1. CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化。

  2. SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。

  3. CONDITION:值为-2,与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

  4. PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态

    2. prev

    前驱节点

    3. next

    后继节点

    4. thread

    thread 同步线程队列主要存储的线程信息。

    5. nextwaiter

    AQS中阻塞队列采用的是用双向链表保存,用prve和next相互链接。而AQS中条件队列是使用单向列表保存的,用
    nextWaiter来连接。阻塞队列和条件队列并不是使用的相同的数据结构

精髓原帖

在Node节点的源码中有两个常量属性

// 共享模式
static final Node SHARED = new Node();
// 独占模式
static final Node EXCLUSIVE = null;
// 其他模式
// 其他非空值:条件等待节点(调用Condition的await方法的时候)

NIO

Channel(通道),Buffer(缓冲区), Selector

Channel

通道是双向的,通过一个Channel既可以进行读,也可以进行写

Buffer

  • capacity:缓冲区数组的总长度

  • position:下一个要操作的数据元素的位置

  • limit:缓冲区数组中不可操作的下一个元素的位置:limit<=capacity

  • mark:用于记录当前position的前一个位置或者默认是-1

Selector

Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理

http 和 https

HTTPS和HTTP的主要区别

  • https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。

  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。

  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  • http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全