一些反复思考需要弄懂细节的问题
无体系,乱七八糟的摘抄和积累,仅供个人学习。
1、锁的本质?
(所谓的锁,在计算机里本质上就是一块内存空间,当这个空间被赋值为1的时候表示加锁了,被赋值为0的时候表示解锁了。多个线程抢一个锁,就是抢着要把这块内存赋值为1。锁:是给cpu的寄存器写值,写值后cpu就不会响应软中断,解锁后又能响应软中断,因此要求加锁后不能做耗时操作或者死锁。)
①临界资源:一次仅允许一个进程使用的资源。例如:物理设备中的打印机、输入机和进程之间共享的变量、数据。
②临界区:每个进程中,访问临界资源的那段代码。
③互斥锁的底层原理是什么?为什么一个线程拿到锁之后,另一个线程就无法获取锁?
互斥锁mutex本质分为两个部分:
Step1:原子的“compare and set”操作
互斥锁是对临界区的保护,能否进入临界区即能否获取到锁,思路都是判断并置位一个标志位,这个标志位本身可以是一个普通的内存变量,关键在于,对于这个变量的“判断并置位”操作需要是原子的。
Step2:根据锁是否获得,来决定执行什么策略
如果锁持有,那么当然是继续往下,执行,如果锁没有获取到,如何处理呢?各种mutex以及变形的实现的花样主要就在这里了,常规的做法是获取不到则将当前任务挂起,并附在mutex变量对应的链表上,一旦有其他获取到锁的任务释放锁时,就查找锁上挂起的任务并唤醒。
还有就是如果获取不到就回头再检查一次,如此反复,直到有一次突然发现可以持有了,这个其实就是自旋锁的实现思路。
④lock提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁。
Object lock = new Object();
synchronized(lock) { //加锁
//... 逻辑 ...
} //解锁
Lock lock = new ReentrantLock();
lock.lock(); //加锁
try {
//... 逻辑 ...
} finally {
lock.unlock(); //解锁
} - 既然两者都是锁,那锁住的是什么呢?是如何标记的呢?
synchronized锁住的是普通对象或类对象,当一个对象被锁时,就在它的对象头中进行一个标记;
lock锁住的是Lock对象,当调用它的lock方法时,会将Lock类中的一个标志位state加1(state其实是AQS这个类中的一个变量,它是Lock中一个内部类的父类),释放锁时是将state减1(加1减1这样的操作是为了实现可重入)。
- Monitor与java对象以及线程是如何关联的?
- Sychronized同步方法和同步代码块
2、用户态和内核态?
系统调用是主动切换,异常和外中断是被动切换。
重量级锁:系统调用(内核函数)引起的内核态与用户态切换,线程切换引起的内核态与用户态切换。
从锁说到重量级锁,再从重量级锁分析用户态和内核态,重量级锁争抢锁+释放锁的C++代码里会调用一些内核函数,期间肯定要涉及到cpu从JVM(用户态)到内核态的来回切换——这种切换会带来大量的系统资源消耗,所以说monitor是一个重量级锁。
线程挂起(park)和挂起线程的唤醒(unpark)本身就是在进行用户态和内核态的切换。
3、TCP的可靠性是怎么实现的?
可靠性不仅仅指数据在传输过程中不被修改,还指数据成功抵达、有序投递、数据不重复。
1,数据被篡改导致对端接收进行checksun校验失败后,会丢弃此包,发送端重传;
2,数据传输过程丢包或延迟,发送端重传;
3,发送端给每个报文标记了SeqN,接收端根据此字段排序,保证有序;
4,因延迟,重复抵达的数据会被接收端抛弃。不重复。
2,数据传输过程丢包或延迟,发送端重传;
3,发送端给每个报文标记了SeqN,接收端根据此字段排序,保证有序;
4,因延迟,重复抵达的数据会被接收端抛弃。不重复。
所谓可靠传输是指在通讯时常见的丢包,乱序的条件下依然可以保障数据被依序接受,中间不丢数据。是靠序列号,滑动收发窗口,接收方ACK和SACK,发送方重传等机制保障的。
TCP利用流量控制解决接收端处理数据不及时导致接收缓冲区被填满后丢失数据的问题;
(流量控制的应对方法无非两种,抑制发送端和增加接收端缓冲区,显然增强缓冲区不可取,TCP采用了滑动窗口机制来抑制发送端的发包速率。)
拥塞控制则用来解决网络拥堵时,过多的数据包进入网络进一步加剧拥堵引起网络风暴的问题。
(拥塞控制算法:慢启动,拥塞避免,快重传与块恢复)
狭义:超时重传;
广义:拥塞控制、流量控制、校验和等。
查看14道真题和解析
