题目来自牛客, 题的答案都是我个人理解的或网上的参考,仅仅只作为复习用.因个人水平有限,不合理的地方请多多指正.
数据库的ACID
(1) A 原子性(atomicity) : 事务是一个不可分割的工作单位,事务中的操作要么都修改,要么都不修改。
(2) C 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。
(3) I 隔离性(isolation):一个事务的执行不能被其他事务所影响。
(4) D 持久性(Durability): 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的.
脏读和幻读是什么?
脏读
脏读是指当一个事务正在访问数据,并且对数据进行了修改。而这种修改还没有提交到数据库中,这时,另外一个事务也访问了这个数据,然后使用了这个数据。
幻读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到了表中的全部数据行。同时,第二个事务也修改了这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样。
不可重复读
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
Innodb和MyISAM的默认隔离级别、能解决幻读跟脏读吗?
不能解决幻读. Innodb的默认隔离级别是 可重复读. 该级别能避免脏读,但是不能避免幻读
B树和B+树的区别。
B树
即二叉搜索树:
1.所有非叶子结点至多拥有两个儿子(Left和Right);
2.所有结点存储一个关键字;
3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;
B+树
B+树是B-树的变体,也是一种多路搜索树:
1.其定义基本与B-树同,除了:
2.非叶子结点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树
(B-树是开区间);
5.为所有叶子结点增加一个链指针;
6.所有关键字都在叶子结点出现;
B+的搜索与B树也基本相同,区别是B+树只有达到叶子结点才命中(B树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
B+树的优点:
1. 非叶子节点不会带上ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。
2. 叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。
B树的优点:
对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。
TCP的三次握手、四次挥手是什么?
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
四次挥手
任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。
如何创建线程
- 继承 Thread
- 实现 Runable
- 实现 Callable
- Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活
- Runnable: 实现接口,比Thread类更加灵活,没有单继承的限制
- Callable: Thread和Runnable都是重写的run()方法并且没有返回值,Callable是重写的call()方法并且有返回值并可以借助FutureTask类来判断线程是否已经执行完毕或者取消线程执行
- 当线程不需要返回值时使用Runnable,需要返回值时就使用Callable,一般情况下不直接把线程体代码放到Thread类中,一般通过Thread类来启动线程
- Thread类是实现Runnable,Callable封装成FutureTask,FutureTask实现RunnableFuture,RunnableFuture继承Runnable,所以Callable也算是一种Runnable,所以三种实现方式本质上都是Runnable实现
既然你提到了线程池,聊聊有哪几种线程池。
newCachedThreadPool :
可缓存线程池,若线程池长度超过处理需要,则回收空线程,否则创建新线程,线程规模可无限大。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
- 当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
newFixedThreadPool :
定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
- 1定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
newScheduledThreadPool :
定长线程池,支持定时及周期性任务执行,类似Timer。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
- 1使用实例:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//表示延迟1秒后每3秒执行一次。
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
newSingleThreadExecutor :
单线程 的线程池,支持FIFO, LIFO, 优先级策略。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
通过观察源码,其中四种线程的创建都是创建一个ThreadPoolExecutor。其中ThreadPoolExecutor是ExecutorService接口的实现类。
刚刚你提到了拒绝策略,那这些策略在什么情况下会用到?
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
讲讲常用的List(ArrayList、LinkedList、顺带提了一下CopyOnWriteArrayList),链表底层怎么查找元素的?(这个只想到迭代)
常用的Map(讲了HashMap、TreeMap、LinkedHashMap的主要特点,还讲了ConcurrentHashMap1.7 1.8怎么上锁的,顺便鞭尸了一下HashTable)
我前几篇面试题文章 题重复了. 略
你刚才讲到了CAS,讲讲什么是CAS。(我直接拿AtomicInteger里的getAndIncrement来讲了,底层是调用unsafe的getAndAddInt去自旋)
cas: compare and swap(比较与交换)也叫自旋锁,或者无锁,或者乐观锁
CAS实现是由三个操作数来完成的, 1:读取的变量值 M 2:旧的预期值A 3:新的修改值B (以下简称)
CAS更新条件: 当CAS进行修改操作的时候, 当且仅当M=A,才会把M修改为B,否则什么都不做。
乐观锁详细介绍我以前的博客有写过: https://blog.csdn.net/wang5701071/article/details/108488834