偏向锁,自旋锁,轻量级锁,重量级锁通过synchronized加锁,第一个线程获取的锁为偏向锁,这时有其他线程参与锁竞争,升级为轻量级锁,其他线程通过循环的方式尝试获得锁,称自旋锁。若果自旋的次数达到一定的阈值,则升级为重量级锁。需要注意的是,在第二个线程获取锁时,会先判断第一个线程是否仍然存活,如果不存活,不会升级为轻量级锁。2、LockReentrantLock基于AQS(AbstractQueuedSynchronizer)实现,主要有state(资源)+FIFO(线程等待队列)组成。公平锁与非公平锁:区别在于在获取锁时,公平锁会判断当前队列是否有正在等待的线程,如果有则进行排队。使用lock()和unLock()方法来加锁解锁。ReentrantReadWriteLock同样基于AQS实现,内部采用内部类的形式实现了读锁(共享锁)和写锁(排它锁)。非公平锁吞吐量高在获取锁的阶段来分析,当某一线程要获取锁时,非公平锁可以直接尝试获取锁,而不是判断当前队列中是否有线程在等待。一定情况下可以避免线程频繁的上下文切换,这样,活跃的线程有可能获得锁,而在队列中的锁还要进行唤醒才能继续尝试获取锁,而且线程的执行顺序一般来说不影响程序的运行。3、volatileJava内存模型
在多线程环境下,保证变量的可见性。使用了volatile修饰变量后,在变量修改后会立即同步到主存中,每次用这个变量前会从主存刷新。禁止JVM指令重排序。单例模式双重校验锁变量为什么使用volatile修饰?禁止JVM指令重排序,newObject()分为三个步骤:申请内存空间,将内存空间引用赋值给变量,变量初始化。如果不禁止重排序,有可能得到一个未经初始化的变量。4、线程的五种状态1).New一个新的线程被创建,还没开始运行。
2).Runnable一个线程准备就绪,随时可以运行的时候就进入了Runnable状态。
Runnable状态可以是实际正在运行的线程,也可以是随时可以运行的线程。
3).Blocked例如一个线程在等待I/O资源,或者它要访问的被保护代码已经被其他线程锁住了,那么它就在阻塞Blocked状态,这个线程所需的资源到位后就转入Runnable状态。
4).Waiting(无限期等待)如果一个线程在等待其他线程的唤醒,那么它就处于Waiting状态。以下方法会让线程进入等待状态:
以下方法会让线程进入有限等待状态:
Thread.sleep(sleeptime)Object.wait(timeout)Thread.join(timeout)LockSupport.parkNanos(timeout)LockSupport.parkUntil(timeout)6).Terminated一个线程正常执行完毕,或者意外失败,那么就结束了。
4)、线程池拒绝策略(默认抛出异常)|:---|:---||AbortPolicy|抛出RejectedExecutionException||DiscardPolicy|什么也不做,直接忽略||DiscardOldestPolicy|丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置||CallerRunsPolicy|直接由提交任务者执行这个任务|
5)、如何根据CPU核心数设计线程池线程数量IO密集型2nCPU计算密集型nCPU+1其中n为CPU核心数量,可通过Runtime.getRuntime().availableProcessors()获得核心数:。为什么加1:即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。四、Java虚拟机1、Java内存结构
堆由线程共享,存放new出来的对象,是垃圾回收器的主要工作区域。栈线程私有,分为Java虚拟机栈和本地方法栈,存放局部变量表、操作栈、动态链接、方法出口等信息,方法的执行对应着入栈到出栈的过程。方法区线程共享,存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等信息,JDK1.8中方法区被元空间取代,使用直接内存。2、Java类加载机制
加载加载字节码文件。链接验证验证字节码文件的正确性。准备为静态变量分配内存。解析将符号引用(如类的全限定名)解析为直接引用(类在实际内存中的地址)。初始化为静态变量赋初值。双亲委派模式
当一个类需要加载时,判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的loadClass()处理,因此所有的请求最终都应该传送到顶层的启动类加载器BootstrapClassLoader中。当父类加载器无法处理时,才由自己来处理。当父类加载器为null时,会使用启动类加载器BootstrapClassLoader作为父类加载器。
3、垃圾回收算法Mark-Sweep(标记-清除)算法标记需要回收的对象,然后清除,会造成许多内存碎片。Copying(复制)算法将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。Mark-Compact(标记-整理)算法(压缩法)与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。GenerationalCollection(分代收集)算法分为年轻代和老年代,年轻代时比较活跃的对象,使用复制算法做垃圾回收。老年代每次回收只回收少量对象,使用标记整理法。4、典型垃圾回收器CMS
a)虚拟机栈中引用的对象(栈帧中的本地变量表);b)方法区中类静态属性引用的对象;c)方法区中常量引用的对象;d)本地方法栈中Native方法引用的对象。
五、MySQL(InnoDB)1、聚簇索引与非聚簇索引
3、最左前缀原则在MySQL中,可以指定多个列为索引,即联合索引。比如index(name,age),最左前缀原则是指查询时精确匹配到从最左边开始的一列或几列(name;name&age),就可以命中索引。如果所有列都用到了,顺序不同,查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
4、什么情况下可以用到B树索引(1)定义有主键的列一定要建立索引。因为主键可以加速定位到表中的某行
(2)定义有外键的列一定要建立索引。外键列通常用于表与表之间的连接,在其上创建索引可以加快表间的连接
(3)对于经常查询的数据列最好建立索引。
5、事务隔离级别Readuncommitted读未提交,可能出现脏读,不可重复读,幻读。Readcommitted读提交,可能出现不可重复读,幻读。Repeatableread可重复读,可能出现脏读。Serializable可串行化,同一数据读写都加锁,避免脏读,性能不忍直视。InnoDB默认隔离级别为可重复读级别,分为快照度和当前读,并且通过行锁和间隙锁解决了幻读问题。
详细参考:《脏读、幻读和不可重复读》
2、Bean生命周期简单来说四步:
实例化Instantiation属性赋值Populate初始化Initialization销毁Destruction在这四步的基础上面,Spring提供了一些拓展点:
Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中%3Cbean%3E的init-method和destroy-method指定的方法Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现,一般称它们的实现类为“后处理器”。工厂后处理器接口方法:这个包括了AspectJWeavingEnabler,ConfigurationClassPostProcessor,CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。3、SpringAOP实现方式两种:
JDK动态代理:带有接口的对象,在运行期实现CGlib静态代理:在编译期实现。4、Spring事务传播行为默认PROPAGATION_REQUIRED,如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
5、SpringIoC
6、SpringMVC工作流程
七、计算机网络1、TCP/IP五层模型
2、浏览器输入地址后做了什么?
3、三次握手与四次挥手三次握手
四次挥手
4、TIME_WAIT与CLOSE_WAIT
5、TCP滑动窗口TCP流量控制,主要使用滑动窗口协议,滑动窗口是接受数据端使用的窗口大小,用来告诉发送端接收端的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量控制的目的。这个窗口大小就是我们一次传输几个数据。对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才允许被发送;同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才允许接收。
6、TCP粘包和拆包
现象产生原因1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。2、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。3、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。解决方式1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。八、MQ消息队列1、场景作用削峰填谷,异步解耦。
2、如何保证消息不被重复消费呢?这个问题可以换个思路,保证消息重复消费,其实是保证程序的幂等性。无论消息如何重复,程序运行的结果是一致的。比如消费消息后做数据库插入操作,为了防止消息重复消费,可以在插入前先查询一下有没有对应的数据。
3、怎么保证从消息队列里拿到的数据按顺序执行?消费端在接收到消息后放入内存队列,然后对队列中的消息进行有序消费。
首先排查消费端问题,恢复消费端正常消费速度。然后着手处理队列中的积压消息。停掉现有的consumer。新建一个topic,设置之前10倍的partation,之前10倍的队列。写一个分发程序,将积压的消息均匀的轮询写入这些队列。然后临时用10倍的机器部署consumer,每一批consumer消费1个临时的队列。消费完毕后,恢复原有架构。消息队列满了:只能边接收边丢弃,然后重新补回丢失的消息,再做消费。
4、如何保证消息的可靠性传输(如何处理消息丢失的问题)?kafka为例:
Hash常用命令:hget,hset,hgetall等
List常用命令:lpush,rpush,lpop,rpop,lrange等
可以通过lrange命令,就是从某个元素开始读取多少个元素,可以基于list实现分页查询。
Set常用命令:sadd,spop,smembers,sunion等
SortSet常用命令:zadd,zrange,zrem,zcard等
2、Redis如何实现key的过期删除?定期删除和惰性删除的形式。
Redis自带发布订阅功能,基于publish和subscribe命令。使用List存储消息,lpush,rpop分别发送接收消息。十、NginxNginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。Nginx主要提供反向代理、负载均衡、动静分离(静态资源服务)等服务。
1、正向代理和反向代理正向代理代理客户端访问服务器。典型:VPN反向代理代替服务器接收客户端请求,然后转发给服务器,服务器接收请求并将处理的结果通过代理服务器转发给客户端。2、负载均衡将请求分摊到多台机器上去,高并发,增加吞吐量。
负载均衡算法权重轮询fairip_hashurl_hash3、动静分离动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。
4、Nginx四个组成部分Nginx二进制可执行文件:由各模块源码编译出一个文件Nginx.conf配置文件:控制Nginx行为acess.log访问日志:记录每一条HTTP请求信息error.log错误日志:定位问题