[1]. 进程和线程的区别
进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。
同一进程的线程共享本进程的内存空间和资源,而进程之间的内存空间和资源是相互独立的,因此线程之间的切换开销要小于进程。
进程可以独立执行,因为它们拥有独立的内存空间和资源;而线程不能独立执行,因为它们共享进程的内存空间和资源。
一个线程崩溃通常会影响整个进程,而一个进程崩溃不会影响其他进程。
[2]. 线程间如何通信?
线程之间的通信实现方式主要有两种:
共享内存:多个线程可以访问同一个共享内存区域,通过读取和写入内存中的数据来进行通信和同步。
消息传递:多个线程之间通过消息队列、管道、信号量等机制来传递信息和同步状态。
[3]. volatile关键字解决什么问题
volatile是轻量级的同步机制,可以用来解决可见性和有序性问题。
使用volatile 关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile 变量,一定是最新的数据。
编译器和处理器在优化代码时可能会进行指令重排序,这可能会出现一些意想不到的问题,使用 volatile 关键字可以禁止编译器和处理器对指令进行重排序,从而保证程序的正确性。
[4]. 操作系统里面怎么保证CAS操作的原子性?
总线锁定:通过CPU提供的LOCK#信号,锁定总线,阻止其他CPU对内存的操作,从而保证CAS操作的原子性。
缓存锁定:通过锁定缓存行,当某个CPU对缓存行中的共享变量进行修改时,其他CPU会通过总线嗅探机制感知到变化,并使自己对应的缓存行失效,从内存重新读取最新数据。
[5]. HTTP的状态码了解吗?
1XX:信息性状态码,表示接收的请求正在处理。
2XX:成功状态码,表示请求正常处理完毕。
3XX:重定向状态码,表示客户端需要采取进一步操作以完成请求。
4XX:客户端错误状态码,表示客户端提交的请求存在错误。
5XX:服务器错误状态码,表示服务器在处理请求时遇到问题。
[6]. HTTP跟HTTPS有什么区别
HTTP 的端口号是 80,HTTPS 的端口号是 443。
HTTP协议以明文方式发送内容,不提供任何方式的数据加密,容易被攻击者截取信息。HTTPS则在TCP和HTTP网络层之间加入了SSL/TLS安全协议,使得报文能够加密传输,保证了数据的安全性。
HTTPS需要使用CA(证书颁发机构)颁发的证书来进行加密和解密操作,而HTTP则不需要证书。
[7]. 三次握手而不是两次握手的原因?四次挥手少一次行不行?
-
避免历史连接;
-
确保双方的初始序列号能被可靠的同步;
-
避免建立多个冗余的无效链接,造成不必要的资源浪费;
在下面这种情况下,三次挥手也是可行的:
客户端请求断开连接后,服务端可能还存在没有传输完的数据,因此需要等待所有数据发送完毕再请求断开,也就是服务端的ACK和FIN报文是分两次发送的。但是,当服务端在收到客户端的请求断开连接FIN报文的时候已经不需要再发送数据了,那么第二次挥手和第三次挥手就可以合并,从而实现三次挥手。
[8]. TCP滑动窗口的工作机制
TCP在每个ACK包中,通知对方自己目前能接收多少数据,即TCP头部中的窗口大小。
发送方可以在这个窗口大小内,连续发送多个数据包,而不必等待每个数据包的确认。
当发送方收到接收方的ACK确认,窗口就会向前滑动,允许发送方继续发送新的数据包。
[9]. TLS的握手过程使用了哪些加密算法?
TLS(Transport Layer Security)的握手过程中,使用的主要加密算法为对称加密算法、非对称加密算法和哈希算法。
其中,常见的对称加密算法包括AES(Advanced Encryption Standard)、3DES(Triple Data Encryption Algorithm)和RC4(Rivest Cipher 4)等。
常见的非对称加密算法包括RSA、Diffie-Hellman和ECDH(Elliptic Curve Diffie-Hellman)等。
常见的哈希算法包括SHA-256(Secure Hash Algorithm 256-bit)和MD5(Message-Digest Algorithm 5)等。
[10]. 手撕:找到数组中右边第一个比它大的数的下标
public int[] FindRightMax(int[] arrs) {
// 如果输入数组为空或为空数组,则直接返回一个长度为0的数组
if (arrs == null || arrs.length <= 0) {
return new int[0];
}
int n = arrs.length;
// 初始化一个栈
Stack<Integer> st = new Stack<>();
// 初始化一个与输入数组长度相同的数组,用于存储结果数组
int[] rightMax = new int[n];
// 从数组的最后一个元素开始向前遍历
for (int i = n - 1; i >= 0; i--) {
int x = arrs[i]; // 获取当前遍历到的元素
// 当栈不为空且当前元素大于等于栈顶元素时,弹出栈顶元素
while (!st.isEmpty() && x >= arrs[st.peek()]) {
st.pop();
}
// 将当前元素的右侧第一个比它大的元素的索引赋值给rightMax数组对应位置
// 如果栈为空,说明当前元素右侧没有比它大的元素,将索引设为数组长度n
rightMax[i] = st.isEmpty() ? n : st.peek();
// 将当前元素的索引压入栈中
st.push(i);
}
return rightMax; // 返回结果数组
}