你的位置:首页 > Java教程

[Java教程]总结秋招的一些面经

  17的秋招已经结束啦,自己也拿到offer了,然后把自己在准备秋招过程中,自己面试中问到的也有,自己面试前准备然后再网上查找的也有,所有的面经放在这里,都是一个个小的知识点,以后自己也可以看看,像jvm,线程池这种知识点,展开就是厚厚的一本书的,可以根据一个小问题去深入了解java基础中的一些构件。所以记录还是有必要的吧,哈哈。


java 锁的补充:

ReadWriteLock (ReadLock / WriteLock)
-> ReentrantReadWriteLock (同一线程对锁的重复获取)

写锁的降级 / 读锁的升级

公平锁 / 非公平锁(synchronized使用非公平) (公平锁维护队列,效率低)

自旋锁 (在 while 循环中一直判断某个条件,直到通过条件时才进入下个临界)

===============================

 

volatile 不加时怎么通知另一个线程读取刷新的数据
两个线程 (这个问题应该是从 JMM 的方面去回答)

问题的答案 :
JMM决定一个线程对共享变量的写入何时对另一个线程可见

JMM是什么的问题:
JMM 就是 线程保存的本地变量副本和主内存之间进程通信的机制, 可以适当涉及到 happens-before/volatile/synchronized/重排序方面的知识
happen-before : 讲一下传递性, 锁的解锁 happens-before 的加锁


问题解决 --> https://segmentfault.com/a/1190000009828216
JMM模型
==============================

jvm 的各个区域的作用 >共享:
方法区 :被加载的类信息、常量、静态变量 (代码存放编译完成后的信息)
堆 :存放对象实例、数组


线程私有:
虚拟机栈 :基本数据类型的数据,以及对象的引用
本地方法栈 :(native方法)
线程计数器 :执行字节码指示器,指示下一个执行的指令


StackOverflow 和 OutofMemory 的区别
如果线程需要的空间大于允许值,则为StackOverflowError;如果stack空间可以动态增加,但最后内存还是不够,则为OutOfMemoryError。

每一个JVM线程维护自己的JVM stack. JVM stack里面存放 JVM栈帧. 栈帧中存放 数据和中间结果(本地变量数组, 操作符栈, 和对runtime 常量池的引用). 这些数据都比较小(对象都在堆中, 栈帧仅存放对象引用), 所以想单纯通过 在栈帧中存放大数据的方法 去引入StackOverflowError, 基本是不现实的.一般都是因为方法调用嵌套层数过大.

(不断创建对象(位于堆中) -> OutofMemory )

 

==============================


ConcurrentHashMap:
Segment数组 (可重入锁)
HashEntry数组 (存储键值对) -》 修改HashEntry里的元素,需要获取对应的Segment锁


join : 是当前线程等待join线程执行完成之后再继续执行


CountDownLatch 等待N个点完成(构造器中传入的参数N)
countdown 方法减少计数器的值
await 方法阻塞当前线程 (有超时等待的版本)

 

CyclicBarrier 阻塞线程到同步点,知道所有的线程都到达同步点时才继续 (线程调用await方法通知到达同步点)

Semaphore 控制同时访问特定资源的线程数 (acquire方法获取, release方法释放)


线程池: Executor框架

FixedThreadPool 固定线程数量 LinkedBlockingQueue(无界队列)

SigleThreadExecutor 当个worker的池 LinkedBlockingQueue(无界队列)

CachedThreadPool 根据需要创建新线程的线程池 没有容量的SynchronousQueue(每个插入操作必须等待另一个线程的移除操作)

 

ScheduledThreadPoolExecutor extends ThreadPoolExecutor 可以指定多个对应的后台线程数

 

FutureTask Future
代表异步计算的结果 get方法在FutureTask完成阻塞,完成之后获取结果或者返回异常
实现:
AQS 同步框架, 里面封装了 CAS 类似的方法, 维护了一个阻塞队列, 位于头部的队列在执行完成之后通知之后的线程执行


==============================

gc 算法

判断gc:
引用计数 : 引用一次计数器加一
GC Roots Tracing : 根节点往下所搜,没链接的节点就gc


gc算法:
标记-清理 : 标记被回收的,然后清理, 会产生很多内存碎片
标记-整理 : 类似标记-清理, 清理完成通过整理减少碎片
复制 : 开阔相同的内存空间, 把未回收的复制到新的内存中

新生代基本采用复制算法,老年代采用标记整理算法。cms采用标记清理


双亲委派模型: 类加载的请求都会首先被传递到父类类加载器中, 每个加载器的加载的文件路径不同, ----》 目的为了保证jvm的安全性

BootstrapClassLoader c实现的类加载器

ExtensionClassLoader java路径下的ext/lib下的文件

SystemClassLoader 指定的classpath的文件
(各自加载的文件路径不同)

 


自定义: loadclass findclass 类放在特殊的目录下,父类加载不到时,就使用自己的加载器加载

 

jvm中的

Eden区 新生代 新创建的对象存放的位置 触发minor gc

老年代 经历过多次gc之后, 新生代中的对象被放入老年代中 触发 full gc / major gc

 

========================================================================

面试中的那点事:

前端 : jsp(jstl标签, 内置对象) js(选择器)

javase : hashmap hashtable等

javaee : 框架 spring springmvc hibernate和mybatis

数据库 : select语句 mysql的存储引擎(innodb和myisam)

linux : 基本的命令 vim

并发 : 锁(重入锁和synchronized) 原子类(CAS) FutureTask(AQS(CAS和一个队列)) ConcurrentHashMap(原理)

jvm : 结构 (堆,栈, 寄存器, 方法区) 类加载器(双亲委派模型)

gc : 收集算法 判断回收的方式 回收的地方

 

 

---------------------------星期天---------------------------------


ReadWriteLock (ReadLock / WriteLock)
-> ReentrantReadWriteLock (同一线程对锁的重复获取)

写锁的降级 / 读锁的升级

公平锁 / 非公平锁(synchronized使用非公平) (公平锁维护队列,效率低)

自旋锁 (在 while 循环中一直判断某个条件,直到通过条件时才进入下个临界)

===============================

 

volatile 不加时怎么通知另一个线程读取刷新的数据
两个线程 (这个问题应该是从 jmm 的方面去回答)

问题解决 --> https://segmentfault.com/a/1190000009828216
JMM模型
==============================

jvm 的各个区域的作用 >共享:
方法区 :被加载的类信息、常量、静态变量
堆 :存放对象实例、数组


线程私有:
虚拟机栈 :基本数据类型的数据,以及对象的引用
本地方法栈 :(native方法)
线程计数器 :执行字节码指示器,指示下一个执行的指令


StackOverflow 和 OutofMemory 的区别
如果线程需要的空间大于允许值,则为StackOverflowError;如果stack空间可以动态增加,但最后内存还是不够,则为OutOfMemoryError。

每一个JVM线程维护自己的JVM stack. JVM stack里面存放 JVM栈帧. 栈帧中存放 数据和中间结果(本地变量数组, 操作符栈, 和对runtime 常量池的引用). 这些数据都比较小(对象都在堆中, 栈帧仅存放对象引用), 所以想单纯通过 在栈帧中存放大数据的方法 去引入StackOverflowError, 基本是不现实的.一般都是因为方法调用嵌套层数过大.

(不断创建对象(位于堆中) -> OutofMemory )


==============================

gc 算法

判断gc:
引用计数 : 引用一次计数器加一
GC Roots Tracing : 根节点往下所搜,没链接的节点就gc

gc算法:
标记-清理
标记-整理
复制

---------------------------星期一---------------------------------


https://juejin.im/entry/58b7a7e78d6d8100652747fd
------------------------------------------
innnodb 和 myisam:
1,InnoDB不支持FULLTEXT类型的索引
2,InnoDB 中不保存表的具体行数(扫描表),当count(*)语句包含 where条件时,两种表的操作是一样的
3,对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引
4,DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除
5,LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用
6,InnoDB表的行锁也不是绝对的,假如在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表
7,MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少

=====》 Innodb 支持事务处理与外键和行级锁


---------------------------------------------

进程间的通信方式:

1,管道: 半双工(数据只能单向流动),只能在具有亲缘关系的进程间使用,进程的亲缘关系通常是指父子进程关系
2,有名管道:半双工,无亲缘关系进程间的通信。
3,信号量:防止某进程正在访问共享资源时,其他进程也访问该资源
4,消息队列:消息的链表,存放在内核中并由消息队列标识符标识
5,信号:通知接收进程某个事件已经发生。
6,共享内存:被其他进程所访问的内存
7,套接字:不同机器间的进程通信


---------------------------------------------

CopyOnWriteArrayList :
写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。
使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

----------------------------------------------

线程池:
1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出

====》 poolSize -》 corePoolSize -》 队列 -》maximumPoolSize -》 拒绝策略


----------------------------------------------

happens-before:
1.程序顺序规则
2.监视器锁规则
3.volatile变量规则
4.传递性
5.线程启动规则


----------------------------------------------

JMM --》 直接回答 6 个模块就好


----------------------------------------------
类加载器工作机制:
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:
a)校验:检查载入Class文件数据的正确性
b)准备:给类的静态变量分配存储空间
c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。


----------------------------------------------

索引: B- , B+

----------------------------------------------

tcp的三次握手和四次挥手: (老生常谈)

(1)三次握手
1,client发送标志位SYN和随机序列号seq给server,进入SYN_SENT
2,server接受SYN标志获取建立链接请求,发送SYN,ACK和ack为接受seq+1和随机序列号seq
server进入SYN_RCVD状态
3,client接受确认,检查ack是否为seq+1,进入ESTABLISHED状态
发送ACK=1和ack为接受server的seq+1,server确认ack是否为自己发送的序列号值+1,
server进入ESTABLISHED状态


为什么三次握手:
为了防止已失效的连接请求报文段突然又传送到了服务端,造成server等待client连接而浪费资源

 

(2)四次挥手
1, client发送FIN client进入FIN_WAIT_1状态
2, server接受FIN 发送ack为接受的FIN值+1,server进入CLOSE_WAIT状态 client进入FIN_WAIT_2状态
3, server发送FIN 关闭server到client的连接,server进入LAST_ACK状态
4, client接受FIN client进入TIME_WAIT 发送ACK=1和ack为接受server的FIN+1,server进入CLOSED状态

为什么四次挥手:
client端发送关闭连接请求,不发送数据但还是能接受数据,
此时server端等待数据全部发送完成之后再发送关闭请求关闭连接

 

----------------------------------------------

输入url之后发生的事情:

1,输入地址
浏览器匹配输入的地址,或者有些直接从缓存获取网页


2,浏览器查找域名的ip地址
(1)从hosts文件里查找对应的域名对应ip的记录,若有,则使用
(2)想本地DNS服务器发送一个DNS请求
(3)本地DNS服务器查找缓存中的记录,若有则返回结果,
若没有,递归向根DNS服务器查询
(4)根DNS服务器给给本地DNS服务器相对应的域名服务器的地址,进行迭代查询
(5)本地DNS服务器向域名服务器发送请求,获取域名解析服务器的地址
(6)域名解析服务器返回域名和ip对应关系,本地DNS服务器返回用户的同时
将对应关系缓存在本地DNS服务器中,以备之后查询


DNS两种查询:
(1)递归解析
DNS服务器自身不能解析,向根域名查询,再由根域名服务器一级一级向下查询

(2)迭代解析
DNS服务器自身不能解析,其他DNS服务器告诉能解析该域名的DNS服务器的ip地址,
让DNS服务器自身再发请求去查询

3, 浏览器向web服务器发送HTTP请求
TCP三次握手 四次挥手 看上面

4, 服务器的永久重定向响应
为什么重定向:
所搜引擎把同一个网站不同的地址定向到相同的网站去

301 :旧地址已经不存在, 新旧地址都到重定向的页面去
302 :旧地址存在, 搜索引擎抓取新地址而保持旧的网址


5, 浏览器跟踪重定向地址
浏览器访问地址了

6, 服务器处理请求
建立与服务器的连接,等待服务器返回结果

7,服务器返回HTTP响应

8,浏览器显示HTML

9,浏览器发送请求获取嵌在HTML中的图片等资源

----------------------------------------------

(https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html)


java nio

BIO :
每次来一个请求(顾客),就分配到线程池中由一个线程处理,如果超出了线程池的最大上限,就扔到队列等待 。

nio :
mainReactor线程负责监听server socket,accept新连接,并将建立的socket分派给subReactor;subReactor可以是一个线程,也可以是线程池(一般可以设置为CPU核数),负责多路分离已连接的socket,读写网络数据

 

-----------------------------------------------

编程的题目都汇总在这了:

KMP

树的遍历

(关于树, 这 >讲解了许多题目, 看一下

给先序后序然后构建二叉树


快排

单例五个方式

TOPK

45度打印二维数组

快排 :

堆排序

dijkstra : >BFS

DFS


二叉树的高度(也有递归和非递归之分)


重构二叉树


最长公共子序列

 

---------------------------星期二---------------------------------

ArrayList扩容,HashMap扩容怎么实现

HashMap: (java8已经变成红黑树了,有空看一下吧)
两个参数 :初始容量 和 加载因子 (默认为16,和0.7)
每次增加元素,判断当前容量是否到达 当前容量*装载因子的大小
超过时,发生rehash操作,容量变为原来的2倍,原来的元素再经过hash放入新的桶数组中
(rehash操作判断原来容量是否为Integer.MAX_VALUE,若是,则直接把threshold设置为Integer.MAX_VALUE)

--》可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap

ArrayList:
默认大小为10
扩容为原来大小的1.5倍+1
原来的数据通过 Arrays.copyOf 方法复制


------------------------------

TreeMap:
实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,
key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常

(实现原理)

------------------------------
java8 中的 HashMap: 数组+链表+红黑树 (链表大于8时转化成红黑树 。。。 (红黑树的增删改,心好累。。))
Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)
采用链地址法, 就是数组加链表的结合,对键hash之后放在对应桶数组下标的链表中

获取桶数组索引位置:
通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方
h& (length-1)运算等价于对length取模,也就是h%length

jdk1.8中 hash算法: 通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16)

-------------------------------

线程有多少种状态 (5种状态)

1,
新建状态:新创建了一个线程对象。

2,
就绪状态:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3,
运行状态:就绪状态的线程获取了CPU,执行程序代码。

4,
阻塞状态:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(1)等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

(2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(3)其它阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5,
死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


-------------------------------


一致性哈希 。。。什么鬼

-------------------------------


图的的环检测(深搜)

有向图(DFS)

/*
* 1,白点集合,里面放还没explore的点
* 2,灰点集合,里面放正在explore的点,当前的灰点们表示一条正在explore的路径,这个路径上的每个点都是灰的
* 3,黑点集合,里面放已经explore的点且这些点不构成环
*
* 黑色节点表示该节点的所有的邻接节点都已经被访问过了
* 若将要访问的点是个灰点,则表示发现了环
*/

public boolean hasCycleDirectedGraph(int n, int[][] edges) {// 前指后
HashSet<Integer> black = new HashSet<Integer>();
HashSet<Integer> white = new HashSet<Integer>();
HashSet<Integer> gray = new HashSet<Integer>();
List<List<Integer>> adjList = new ArrayList<List<Integer>>();
// 这里是在为每个节点添加一个表达临近节点的集合
for (int i = 0; i < n; ++i) {
white.add(new Integer(i));
adjList.add(new ArrayList<Integer>());
}
// 这里是在 设置临近节点的集合
for (int[] edge : edges) {
adjList.get(edge[0]).add(new Integer(edge[1]));
}
//
for (int i = 0; i < n; i++) {
if (white.contains(i)) {
if (hasCycle(i, white, gray, black, adjList))
return true;
}
}
return false;
}

private boolean hasCycle(Integer vertex, HashSet<Integer> white, HashSet<Integer> gray, HashSet<Integer> black,
List<List<Integer>> adjList) {
white.remove(vertex);
gray.add(vertex); // current vertex is being visited
for (Integer succ : adjList.get(vertex)) { // successors of current
// vertex
if (white.contains(succ)) {
if (hasCycle(succ, white, gray, black, adjList)) {
return true;
}
} else if (gray.contains(succ)) {
return true;
} else if (black.contains(succ)) {
continue;
}
}
gray.remove(vertex);
black.add(vertex);
return false;
}

 

无向图 (并集查找 DFS)


并集查找:

/*
* 开始这个图只有顶点,没有边,我们来一条一条的添加边。
* 每遇到一条边,判断这边的两个端点是否在同一个集合里?
*
* 在的话,表示有环:因为两个点在一个集合里就表示这两个点已经有一条路径了,现在再加一条路径,必然构成环。
* 不在的话,表示不构成环,我们应该合并这两个集合:因为加上这条边,两个集合就被连起来了,合并成了一个集合
*
*
*/
public static void main(String[] args) {
int[][] edges = { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 1, 3 }, { 1, 4 } };
int n = 5;
UnDirectCircle directCircle = new UnDirectCircle();
System.out.println(directCircle.validTree(n, edges));
}

public boolean validTree(int n, int[][] edges) {
UnionFind uf = new UnionFind(n);
for (int[] edge : edges) {
int p = edge[0];
int q = edge[1];
if (uf.find(p) == uf.find(q))
return false;
else
uf.union(p, q);
}
return uf.count() == 1;
}
}

class UnionFind {
private int[] father;
private int count;

public UnionFind(int n) {
father = new int[n];
count = n;
// 初始化所有节点的父节点为节点本身
for (int i = 0; i < n; i++) {
father[i] = i;
}
}

public int count() {
return this.count;
}

public int find(int p) {
int root = father[p];

// 找到节点的最高的父节点
while (root != father[root])
root = father[root];

// as long as we get here, root is the final dad
// p 当前节点 father[p] 当前节点的父节点 迭代更新当前路径上的所有的节点为最高父节点
while (p != root) {
int tmp = father[p];
father[p] = root;
p = tmp;
}
return root;
}

public void union(int p, int q) {
int fatherP = find(p);
int fatherQ = find(q);
if (fatherP != fatherQ) {
father[fatherP] = fatherQ;
count--;
}
}
}

 

DFS:

public boolean validTree(int n, int[][] edges) {
HashSet<Integer> visited = new HashSet<Integer>();
List<List<Integer>> adjList = new ArrayList<List<Integer>>();
for (int i = 0; i < n; ++i)
adjList.add(new ArrayList<Integer>());
for (int[] edge : edges) {
adjList.get(edge[0]).add(edge[1]);
adjList.get(edge[1]).add(edge[0]);
}
if (hasCycle(-1, 0, visited, adjList)) // has cycle?
return false;
if (visited.size() != n) // is all connected?
return false;
return true;
}

private boolean hasCycle(Integer pred, Integer vertex, HashSet<Integer> visited, List<List<Integer>> adjList) {
visited.add(vertex); // current vertex is being visited
for (Integer succ : adjList.get(vertex)) { // successors of current
// vertex
if (!succ.equals(pred)) { // exclude current vertex's predecessor
if (visited.contains(succ)) {
return true; // back edge/loop detected!
} else {
if (hasCycle(vertex, succ, visited, adjList)) {
return true;
}
}
}
}
return false;
}

 

-------------------------------


缺页操作系统如何处理

每当所要访问的页面不在内存时,会产生一次缺页中断,
此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,
将其调入内存。


  1. 保护CPU现场
  2. 分析中断原因
  3. 转入缺页中断处理程序进行处理
  4. 恢复CPU现场,继续执行

在FIFO算法中,先进入内存的页面被先换出
在LRU算法中,最近最少使用的页面被先换出
在OPT算法中,在最远的将来才被访问的页面被先换出


-------------------------------


简单讲讲操作系统内存管理机制,
段式与页式内存管理的优缺点(顺道讲了下段页式)

 

 

 

-------------------------------

RPC (grpc dubbo thrift)

RPC 工作流程:
(1) client以本地调用方式调用服务
(2) client sub序列化调用请求的方法,参数,并发送到服务端
(3) server stub反序列化请求的方法和参数,并调用本地的服务
(4) server stub序列化返回值,并发送回client stub
(5) client stub反序列化收到的返回结果,并返回给调用的服务


原理 -》 JDK的动态代理 字节码 CGLIB


requestId ---》 解决多个请求情况下 response 对应 request
(1) 线程调用rpc时生成唯一requestId,并且和处理结果的回调对象callback
一起放入到ConcurrentHashMap中

(2) callback的get方法未获取结果则调用wait方法让当前线程等待

(3) server端返回结果时,根据requestId在ConcurrentHashMap中获取处理结果的Callback对象
调用callback的notifyAll唤醒线程


ps : RMI 只支持java应用(返回java对象和基本类型) rpc支持多语言

 


-------------------------------

(https的问题 : >HTTPS如何实现

1, 浏览器将自己支持的一套加密规则发送给网站
2, 网站从中选出一组加密算法与HASH算法,并发送证书信息(包含公钥)
3, 浏览器验证证书的合法性,若信任,生成随机密码,并用公钥加密随机密码,
用约定的HASH算法计算握手消息,并用随机密码对其加密,返回给网站
4, 网站用私钥解密浏览器的随机密码,再用密码解密握手消息,(?这里不懂?)并验证HASH是否一致
网站使用密码加密一段握手消息,发给浏览器
5, 浏览器解密并计算握手消息的HASH,验证与服务端发送的HASH是否一致,握手结束
通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密

 

非对称加密算法用于在握手过程中加密生成的密码,
对称加密算法用于对真正传输的数据进行加密,
而HASH算法用于验证数据的完整性


Point:(客户端产生的对称密钥(随机密码)用非对称加密算法传输, 之后传输的消息使用对称密钥进行加密)
1,服务器下发的内容不可能被伪造,因为别人都没有私钥,所以无法加密。
强行加密的后果是客户端用公钥无法解开。
2,任何人用公钥加密的内容都是绝对安全的,因为私钥只有服务器有,
也就是只有真正的服务器可以看到被加密的原文。

 

-------------------------------


对称加密与非对称加密

对称加密 : 对原始数据的可逆变换 比如对一列数每位数字加一


非对称加密 :有两个秘钥,一个是公钥,一个是私钥。公钥加密的内容只有私钥可以解密,私钥加密的内容只有公钥可以解密

 

--------------------------------

信息安全传输
1,客户端和服务器直接的通信只有自己能看懂
2,客户端和服务器可以校验数据是否被修改过
3,第三方无法冒充服务器

 

--------------------------------


通信网络协议栈

物理层: 实现比特流的透明传输 CSMA/CD

数据传输层: 流数据封装成帧 ARP/RAPR
MAC子层的主要任务是,完成网络介质的访问控制;
LLC子层的主要任务是建立和维护网络连接,执行差错校验、流量控制和链路控制。

网络层: 分组传输 路由选择 ICMP

传输层:通信子网和资源子网的接口和桥梁 提供可靠的端到端的差错和流量控制,保证报文的正确传输 TCP/IP UDP

会话层:向两个实体的表示层提供建立和使用连接的方法 RPC

表示层:处理用户信息的表示问题,如编码、数据格式转换和加密解密

应用层: 应用程序 HTTP FTP SMTP TELNET

 

--------------------------------

TCP拥塞控制,流量控制,

 

--------------------------------

滑动窗口协议,糊涂窗口

 

--------------------------------
 

select和epoll : > 

问题 :
Epoll与Select区别以及epoll优点,
为什么一般情况下epoll性能比select好,ET模式与LT模式

select工作过程: (fd : 文件描述符)
1,调用select函数,将fd_set从用户空间拷贝到内核空间
2,注册回调函数
3,遍历fd,调用对应的poll方法(对于socket,有tcp_poll,udp_poll或者datagram_poll)
4,回调函数把当前进程挂到设备的等待队列中,
当设备收到一条消息(网络设备)或填写完文件数据后(磁盘设备),
会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。
5,poll方法返回读写操作是否就绪的状态码,并赋给fd
6,若遍历所有fd而没有可读写的状态码,调用select的进程调用schedule_timeout休眠一段时间,
等待设备资源可读写后唤醒,重新遍历fd,如此循环
7,把所有fd_set从内核空间拷贝到用户空间

缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024


select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降


epoll epoll_create,epoll_ctl和epoll_wait,
epoll_create是创建一个epoll句柄;
epoll_ctl是注册要监听的事件类型;
epoll_wait则是等待事件的产生

epoll_create 每次注册新的事件到epoll句柄中时 把所有的fd拷贝进内核,保证了每个fd在整个过程中只会拷贝一次
epoll_ctl时把当前进程挂一遍,并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表
epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd

epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目


总结:
1,select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,
epoll只需要在唤醒时判断一下就绪链表是否为空就行了,这节省了大量的CPU时间
2,select,poll每次调用都要把fd集合从用户态往内核态拷贝一次
epoll只要一次拷贝,而且把current往等待队列(epoll内部定义的,不是设备等待队列)上挂也只挂一次

 


epoll 的 LT 和 EL :

1,LT (level triggered)是缺省的工作方式,并且同时支持block和no-block socket.
内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作
如果你不作任何操作,内核还是会继续通知你的

2,EL (edge-triggered)是高速工作方式,只支持no-block socket
当描述符从未就绪变为就绪时,内核通过epoll进行一次通知,之后不再通知

 

--------------------------------

TCP 和 UDP 的区别
??发送3个80字节包,TCP与UDP下对端分别接受几次(其实就是TCP与UDP区别之一,TCP基于流)

都属于 OSI 中的传输层的协议
TCP 面向连接, UDP 无连接
TCP首部开销20字节,UDP首部开销8字节
TCP逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
UDP没有拥塞机制,因此网络出现拥堵不会使源主机的发送效率降低
TCP的连接只能是点到点的,UDP支持一对一,多对一,多对多的交互通信

TCP 应用:Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传输协议)。
TCP用于传输数据量大,可靠性要求高的应用
UDP 应用:NFS(网络文件系统)、SNMP(简单网络管理系统)、DNS(主域名称系统)、

--------------------------------

IPC有哪些,共享内存原理

 

-------------------------------

什么是缓存,为什么需要缓存,有哪些缓存使用场景

缓存是临时存放数据(使用频繁的数据)的地方,介于外部请求和真实数据之间。

1,硬件缓存 硬盘(CPU)与外界接口(通常是内存)之间的暂存器
2,客户端缓存
3,服务端缓存 数据库连接

 

--------------------------------

 

https://yikun.github.io/2015/04/03/%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AALRU-Cache%EF%BC%9F/


LRU cache思路

(1) 基于HashMap和双向链表的实现
获取时从HashMap中获取Node,并更新到当前链表的头部
插入时,从HashMap获取Node,更新节点到链表头部并修改节点的值为新值


(2) 使用 LinkedHashMap 在构造函数传入 缓存容量,装载因子, 访问规则为true
(access-order为true时会更新访问的值到队列头部), 重写 removeEldestEntry方法

ps:关于LinkedHashMap的构造函数的第三个参数:
tt>true</tt> for access-order, <tt>false</tt> for insertion-order

private int capacity;
private Map<Integer, Integer> cache;
public LRUCache(int capacity) {
this.capacity = capacity;
this.cache = new java.util.LinkedHashMap<Integer, Integer> (capacity, 0.75f, true) {
// 定义put后的移除规则,大于容量就删除eldest
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
};
}


--------------------------------


操作系统调度算法

1,先来先服务调度算法(FCFS)
维护任务队列,先进队列的作业或进程先分配资源并创建进程投入运行,
直到完成或阻塞

2,短作业(进程)优先调度算法(SJF)
从队列中获取一个估计执行时间最短的任务,分配资源并执行

3,优先权调度算法
从任务队列中获取优先级最高的任务并执行
(1)非抢占式
选取一个最高优先级的任务后,不能再为另一个优先级的任务分配资源
(2)抢占式
选取一个当前最高优先级的任务后,若有另一个更高优先级的任务进入队列,则重新进行分配

4,高响应比优先调度算法
短作业优先算法 结合 优先权算法
让队列中的长任务在等待过程中不断提高自身的优先级,
保证了长任务由于短任务的执行而没有分配资源的情况

5,时间片轮转法
为任务队列中的作业或进程程分配一个时间段,该时间段内
执行该任务,超时发送中断请求暂停执行


6,多级反馈队列调度算法
设置多个队列,为每个队列分配不同的时间片和不同的优先级,
然后循环执行队列的任务
(最好的算法:不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要)

 

--------------星期天------------------


Nginx 负载均衡算法:
轮询
找最少的连接数
响应最快连接最少的
IP_HASH算法


--------------------------------

回话一致性:

(1) cookie insertion
服务器返回响应后,nginx向客户端植入cookie,客户端带着cookie,Nginx根据cookie转发请求


(2) stiky session
服务器第一次响应后,产生route信息, nginx通过访问route_cookie, route_session 中第一个不为空的
作为route

(3) learn
从已有的session中选择服务器

 

--------------------------------

InputStream 和 Reader 的区别

(1) InputStream是表示字节输入流的所有类的超类 读出来的 byte 数组

(2) Reader是用于读取字符流的抽象类 读出来的 char 或 String


区别
1,InputStreamReader , FileReader 涉及到编码问题, FileInputStream 不会1,
2,BufferReader类用来包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和InputStreamReader)
例如:
1) File file = new File ("hello.txt");
FileInputStream in=new FileInputStream (file);
InputStreamReader inReader=new InputStreamReader (in,"UTF-8");
BufferedReader bufReader=new BufferedReader(inReader);

2) File file = new File ("hello.txt");
FileReader fileReader=new FileReader(file);
BufferedReader bufReader=new BufferedReader(fileReader);

 

--------------------------------


IO和NIO的区别和原理

(1) IO是面向流的,NIO是面向缓冲区的
java io 从流中读取字节,直到读完所有的字节,中间并没有缓存的过程;
java nio 先把数据读取到缓存中,在缓存中对数据进行移动操作,增加处理过程的灵活性


(2) 阻塞与非阻塞IO
当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。直至数据变的可以读取之前,该线程可以继续做其他的事情
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道

(3)
使用单线程Selector来管理多个通道,减少系统开销

 

--------------------------------

NIO中select的实现机制


(1) 创建Selector

(2) 向Selector注册通道

(3) SelectionKey

(4) 通过Selector选择通道

(5) wakeup

(6) close()

 

--------------------------------

synchronized 和 lock 的分区别:

(1) 两者都有相同的并发性和内存语义

synchronized 没有超时等待的机制
lock 有超时机制


(2) ReentrantLock 获取锁定三种方式
1, lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
2, tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
3, tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
4, lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断


(3) synchronized jvm层面上,可以监控,异常时自动释放锁
lock 一定要在 finally中调用unlock方法

 

 

--------------------------------

TCP/IP 协议栈

可分为三个层次:网络层、传输层和应用层。

在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。

在传输层中有TCP协议与UDP协议。

在应用层有:TCP包括FTP、HTTP、TELNET、SMTP等协议

 

---------------------------------

Http 长连接和短连接的区别


短连接: ( ----》服务器的连接 )

连接->传输数据->关闭连接
短连接是指SOCKET连接后发送后接收完数据后马上断开连接

 

长连接: (操作频繁时, TCP的三次握手降低效率 --》 数据库的连接 )
连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
长连接指建立SOCKET连接后不管是否使用都保持连接 (Connection:keep-alive)

 


--------------------------------

web app 是如何初始化spring和struts容器的

 

--------------------------------


servletlistener与fliter你知道多少

 

 

--------------------------------

生产者和消费者模式

 

--------------------------------

jsp 内置对象

request 客户端的请求

response 服务端返回结果

session 服务端创建的与用户请求相关的对象

application 对象中的内容直到应用关闭才会消失

page 当前jsp页面

pageContext 获取其他范围的参数信息(request, response等)

out 在浏览器中输出信息

exception 异常信息

config 获取服务器的配置信息, 在web.

 


--------------------------------

星期天刷的yy的面经:


 

--------------------------------

线程池的使用 :

先回答从 corePoolSize 等待队列 maximunPoolSize 拒绝策略


等待队列:
1, ArrayBlockingQueue 基于数组的有界阻塞队列 FIFO
2, LinkedBlockingQueue 基于链表的阻塞队列 FIFO
3, SynchronousQueue 不存储元素的队列, 插入操作都要等另一个现成调用移除操作,否则插入一直处于阻塞状态
CachedThreadPool使用该队列
4, PriorityQueue 具有优先级的阻塞队列


拒绝策略 :
1, AbortPolicy 直接抛异常
2,CallerRunsPolicy 调用者自己运行该任务
3,DiscardOldestPolicy 丢弃最近的一个任务,并运行当前线程
4,DiscardPolicy 丢弃任务不处理


线程池执行任务:
execute 向线程池提交不需要返回值的任务
submit 提交有返回值的任务


线程池的关闭 :
shutdownNow
: 线程池置为STOP状态,停止所有正在执行的任务或暂停任务的列表,返回等待执行的任务列表
shutdown
: 线程池置为SHUTDOWN状态, 然后中断所有当前不在执行状态的线程

 

Executor框架 :
FixedThreadPool : 固定线程数的线程池

SingleThreadExecutor : 使用单个线程的executor
场景:保证顺序执行任务

CachedThreadPool : 根据需要创建新的线程

 


------------------------------------

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式

策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、
备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

并发型模式和线程池模式


struts中的 ExecutionChain 的责任链模式 OgnlValueStack中的装饰模式 (封装了一个ArrayList的对象)

hibernate的 SessionFacotory 的工厂模式

JDBC的 桥接模式

Spring bean 的单利模式

 

------------------------------------

如何才能产生死锁

产生死锁的四个必要条件:
一.互斥条件:所谓互斥就是进程在某一时间内独占资源。
二.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
三.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
四.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

 

------------------------------------

产生死锁的原因:
一.因为系统资源不足。
二.进程运行推进的顺序不合适。
三.资源分配不当。

 

------------------------------------

 

死锁的预防

打破产生死锁的四个必要条件中的一个或几个,保证系统不会进入死锁状态。
一.打破互斥条件。即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。所以,这种办法并无实用价值。
二.打破不可抢占条件。即允许进程强行从占有者那里夺取某些资源。就是说,当一个进程已占有了某些资源,它又申请新的资源,但不能立即被满足时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。
三.打破占有且申请条件。可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源,所以不会发生占有资源又申请资源的现象,因此不会发生死锁。
四.打破循环等待条件,实行资源有序分配策略。采用这种策略,即把资源事先分类编号,按号分配,使进程在申请,占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源,才能申请大号资源,就不会产生环路,从而预防了死锁。

 

------------------------------------

mysql数据库的各种骚操作:

备份mysql数据
mysqldump -u root -p goods t_admin > D:backup.sql
还原mysql数据
mysql -u root -p < D:backup.sql

查看mysql帮助
mysqldump --help

 


MySQL查询浏览器(MySQL Query Browser)

MySQL管理员(MySQL Administrator) 创建备份 、创建用户并分配权限、

MySQL迁移工具箱(MySQL Migration Tookit) 帮你把数据从别的数据库系统迁移到MySQL里。

MySQL工作台(MySQL Workbench) MySQL的建模工具。

 

查看mysql当前的状态:
(1)SHOW GLOBAL STATUS;
(2)SELECT * FROM information_schema.SESSION_STATUS;

 

(1)QPS (每秒查询量)、TPS(每秒事物量)
(2)增删改查的分量
(3)连接数、正在使用的连接数、最大连接数
(4)慢查询
(5)流量统计
(6)Innodb、MyISAM缓冲池


------------------------------------

聚簇索引和非聚簇索引

create cluster index 创建聚簇索引
create index 创建非聚簇索引


聚簇索引 : 表数据按照索引的顺序来存储,叶子节点储存了真实的数据行
非聚簇索引 :表数据存储顺序与索引顺序无关,叶结点包含索引字段值及指向数据页数据行的逻辑指针


聚集索引是一种稀疏索引,数据页上一级的索引页存储的是页指针,而不是行指针。
非聚集索引,则是密集索引,在数据页的上一级索引页,它为每一个数据行存储一条索引记录。
(这里数据页的上一级的索引页, 可以参考两张图 -》 >聚簇索引:
在增加操作时,根据索引找到对应的数据页,为新数据腾出新空间,然后插入,可能会
造成索引页的拆分,并调整索引指针的操作
(非聚簇索引简单地放到表的末尾位置)

在删除操作时,会将下方数据上移填补空缺,若是最后一页,该数据页会被回收,
可能造成“索引合并”(数据页只有一行数据时,被移到临近的数据页)


聚簇索引使用于对范围值搜索的情况,因为索引值的行在物理相邻


------------------------------------

脏读: 读取到其他事物未提交的额数据
不可重复读: 同一个事物中两次读取到的数据不同
幻读: 事物过程中获取到其他事物新提交的数据

 

 


 

   大概就是这些知识点了,有些是当时偷懒,然后没有去查的,还有一些问题,像在yy面试时被问到的,“如果jvm中只有栈,没有堆,或者只有堆没有栈会怎样”,这问题我到现在还不知道怎么回答。

  有兴趣就一个个的深入了解吧,一些总结,还有附带的链接都是写得很好的文章博客,值得看看。