面试题汇总
文章目录
一、数据结构
前序(根、左、右)
中序(左、根、右)
后序(左、右、根)
参考:https://www.cnblogs.com/llguanli/p/7363657.html
2.
二、网络
methord | 描述 |
---|---|
GET | 请求指定页面信息,并返回实体主体 |
HEAD | 类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头 |
POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。(全部取代) |
PATCH | 从客户端向服务器传送的数据取代指定的文档的内容。(部分取代) |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
响应码 | 说明 | 备注 |
---|---|---|
200 | OK | 请求已成功 |
201 | Created | 资源已创建 |
204 | No Content | 请求已成功,但无返回内容 |
304 | Not Modified | 缓存有效 |
400 | Bad Request | 语义有误,当前请求无法被服务器理解,请求参数错误 |
401 | Unauthorized | 当前请求需要用户认证(登录) |
403 | Forbidden | 用户已认证(登录),但权限不足 |
404 | Not Found | 请求源未在服务器上被发现 |
405 | Method Not Allowed | 请求方法不能被用于请求相应的资源,如使用PUT方法访问只接受POST方法的API |
500 | Internal Server Error | 服务端内部错误 |
502 | Bad Gateway | 网关错误 |
504 | Gateway Timeout | 网关超时 |
根据HTTP协议规定,GET请求用于信息获取,应该是安全的和幂等的(无副作用),虽然获取的结果会变化,但请求不会有任何副作用。
领域 | GET | POST |
---|---|---|
作用 | 查询数据 | 创建、修改数据 |
长度限制 | HTTP协议中无限制 但由于各个浏览器有自己的对于URI的长度限制 因此会有长度限制 |
无限制 |
后退、刷新操作 | 无影响 | 会重复提交数据 前端哪些解决方案 后端哪些解决方案 |
请求过程 | TCP三次握手,并发送数据 1. 浏览器请求TCP连接 2. 服务器响应TCP连接 3. 浏览器确认TCP连接,并发送GET请求头和数据 4. 服务器响应200,OK |
TCP三次握手后,发送数据 1. 浏览器请求TCP连接 2. 服务器响应TCP连接 3. 浏览器确认TCP连接,并发送POST请求头 4. 服务器返回100 continue响应 5. 浏览器发送POST的数据 6. 服务器响应200,OK |
缓存 | 可以缓存 | 无法缓存 |
历史 | 数据会保留在浏览器历史中 | 数据不会有历史 |
数据类型 | ASCII字符 | 无限制 |
安全性 | 差,数据暴露,遗留历史 | 数据不显示,无历史记录 |
(1) URL部分:协议(HTTP\HTTPS)、DNS域名解析过程、资源路径
DNS解析过程:www.google.com
- 浏览器检查自身缓存,域名所对应IP地址
- 浏览器缓存未命中,检查操作系统缓存(hosts文件),是否存在域名对应IP地址
- hosts文件未命中,请求LDNS(本地域名服务器)查询域名
- LDNS查询本地域名解析缓存结果,命中则返回
- LDNS未命中缓存,继续向上级DNS服务器查询,直至查询返回域名信息
(2) TCP/IP、DNS、HTTP、HTML、渲染
三、操作系统
ls、tail -f、less、more、pwd、rm -rf、mv、cp、find、chmod、chown、grep、top、cat、ps -ef|grep tomcat
2.
四、设计模式
分为三类
创建型模式:单例、工厂、抽象工厂、建造者模式、原型模式
结构型模式:适配器模式、过滤器模式、桥接模式、组合模式、装饰器模式、外观模式、代理模式、享元模式
行为型模式:责任链模式、命令模式、解释器模式、迭代器模式、观察者模式、状态模式、空对象模式、策略模式、模板模式、访问者模式、反应器模式
2.
五、Java基础
-
锁的分类
- 公平锁、非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
比如:ReentrantLock,默认是非公平锁,也可以通过构造函数传入公平模式创建公平的可重入锁。而Synchronized只是非公平锁,谁拿到就是谁的,跟申请顺序无关。 - 排他锁、共享锁
排他锁,也叫互斥锁、写锁是指该锁一次只能被一个线程所持有,是一种概念。
共享锁是指该锁可被多个线程所持有,是一种概念。
ReentrantLock是排他锁,ReadWriteLock,其中读是共享锁,写是排他锁。读写,写读 ,写写的过程是互斥的。Synchronized只能是排他锁。 - 可重入锁、读写锁
ReentrantLock,可重入锁,是排他锁的一种实现,加锁后其他线程无法再获取这个加锁对象。ReentrantLock是基于AQS实现,AQS的实现基础即为CAS。
读写锁是一种自旋锁,即不断重试。比如读锁存在,尝试进行写锁时,会不断重试,直至读锁释放,才可以获取到写锁。或当在进行写加锁时,读会不断重试,直至写锁释放。读与读之间不会互斥,会共享锁。
为了防止饿死情况,即读一直存在,写锁拿不到,会在写尝试加锁时,禁止后续的读再拿到共享锁,而是等待读锁释放,由等待的写锁进行操作。适用于读多写少的情况。 - 乐观锁、悲观锁
是指看待并发同步的角度。
乐观锁:认为对于同一个数据的并发操作,一般是不会发生修改的。
悲观锁:认为对于同一个数据的并发操作,一定会发生修改。
乐观锁的实现方案:CAS算法(compare and swap)、自旋
悲观锁的实现方案:Synchronized、ReentrantLock、数据库行锁、表级锁等 - 分段锁
分段锁是一种设计,比如ConcurrentHashMap,采用了分段锁方式实现高效的并发操作。
ConcurrentHashMap结构设计有16个Segment,Segment结构即为HashMap,同时继承了ReentrantLock,从而实现最高支持16个并发操作。 - 自旋锁
一种锁设计,是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
优点:自动重试获取锁,不会阻塞,因为线程一直是active状态,不断循环尝试获得锁,因此无需线程上下文切换。
缺点:可能会导致busy-waiting忙等待,不公平的自旋锁可能会导致线程饥饿,一直获取不到锁。
实现原理:CAS思想,如果compareAndSwap成功,则尝试加锁成功,否则继续循环。
- 公平锁、非公平锁
-
Synchronized的同步原理
synchronized关键字可以修饰普通方法、静态方法、代码块,主要分为代码块的同步和方法的同步两种,两种同步底层原理相同,但是实现方式稍有不同。
底层原理
Monitor对象,任何同步的访问必须先获取monitor对象,才能进行操作,然后再释放monitor对象。
代码块的同步:
指令控制,monitorenter,monitorexit。
monitorenter:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
- 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
- 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数进行加1。
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
monitorexit:
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
方法的同步:
方法的同步分为普通方法和静态方法。静态方法属于类,因此需要获取类锁,每个类只有一个类锁,因此同步的静态方法只能获得锁后调用,非同步的方法可以随意调用。每个类可能会实例化为很多实例,每个实例有一个对象锁,不同对象之间加锁不同。
可以认为类锁也是对象锁,其实类也是一个Class对象,所有静态的都是走的类这个对象。类锁和方法锁之间不冲突,互相无影响。
方法的同步采用的是ACC_SYNCHRONIZED标记,即标志此方法为同步方法。
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。
锁就存在于Java对象头中。
Java对象在内存中保存,由三部分组成:Java对象头,实例数据,对齐填充字节。
Java对象头由三部分组成:Mark Word,指向类的指针,数组长度(只有数组对象才有)。
以下为Mark Word关于锁的状态。默认无锁,标志位为01。
JVM一般是这样使用锁和Mark Word的:
- 当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。
- 当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。
- 当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。
- 当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。
- 偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。
- 轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。
- 自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。
以上即为JVM使用锁的过程。
领域 | Synchronized | ReentrantLock |
---|---|---|
条件 | 只有一个 | 可以使用condition组合灵活控制 |
顺序 | 固定非公平锁 | 公平锁、非公平锁都支持 |
锁超时 | 阻塞等待 | 可以尝试加锁,可以控制等待时间,可以中断 |
操作 | 加锁 | 加锁、finally解锁 |
比如:AtomicInteger、AtomicLong等。线程安全的类。
AtomicInteger中会调用Unsafe类下的CompareAndSwapInt方法,通过CAS的思想与自旋的方式,实现同步非阻塞的方式。
抽象类:AbstractQueuedSynchronizer,抽象的队列式同步器,定义了一套多线程访问共享资源的同步器框架,是java.util.concurrent的核心。
CAS指令,CPU指令,compare and swap,Unsafe类下的cas操作会在虚拟机中进行特殊处理,处理为一条指令信息,通过硬件原子操作实现非阻塞同步,通过冲突检测和操作完成。
CAS需要三个操作数,分别是内存位置(用V表示),旧的预期值(用A表示)和新值(用B表示)。CAS执行时,当且仅当V符合旧预期值A时,处理器用新值B更新V的值,否则它就不执行。
CAS+自旋重试,从而实现乐观锁式的操作。
Unsafe类是java中非常特别的一个类。它名字就叫做“不安全”,提供的操作可以直接读写内存、获得地址偏移值、锁定或释放线程。
通过正常途径是无法获得Unsafe实例的,首先它的构造方法是私有的,然后,即使你调用它的getUnsafe方法,也会抛出SecurityException,因为getUnsafe方法中检查该CallerClass(调用方)是不是由系统类加载器BootstrapClassLoader加载。除非通过反射机制,才能跳过安全检查。
功能:
- 读功能:读取内存中的某块数据
- 写功能:
- CAS操作:compareAndSwapXXX,原子操作,cpu在执行cas时对这一块内存是独占排他的。在并发包中很多操作真正执行的也是cas,并发包中的类并发性能比使用 synchronized 关键字好也在于此:锁的粒度小了许多并且少了线程上下文切换。
- park、unpark:线程挂起、恢复线程。(比如:ReentrantLock,在线程加锁失败时会进行挂起,就是走的park,直至超时或中断)
什么是进程?什么是线程?进程与线程的区别?什么是多线程?多线程解决什么问题?多线程可能会引入哪些问题?
参考:https://blog.csdn.net/yaosiming2011/article/details/44280797
同步、异步是结果,阻塞、非阻塞是过程。
同步、异步是目的,阻塞、非阻塞是实现方式。
调用同步的方法在没有拿到锁的情况下会出现阻塞,在获得锁的情况下不会出现阻塞。
BIO:blocking IO,阻塞式IO。
六、数据库
- 原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
- 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
- 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。
- 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库。
- read-uncommitted,读未提交,导致
- read-committed,不可重复读
- repeatable-read,可重复读
- serializable,串行化
- 脏读:事务A读取到事务B还未提交的新数据,但事务B最后执行失败,回滚了数据,因此事务A读到了错误的脏数据。
- 不可重复读:事务A多次读取同一数据,由于事务B在更新、修改此数据,导致事务A多次读同一数据出现不同结果。(针对某行记录的修改)
- 幻读:事务A统计某一条件的所有数据信息,事务B刚创建一条符合条件的数据,在事务A根据相同条件再查询时,发现统计数据又多了一条,好像出现了幻觉。(针对结果集的删除、新增)
锁是一种方式,并非数据库独有。
多版本并发控制机制
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,分别保存了这个行的创建时间和删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为自增ID),其实是CAS(compare and swap)的思想。
六、开源框架
- RPC框架
- MQ框架
- MQ消息重复如何处理
- MQ如何保证顺序,是否可以并发消费顺序