Laiyong Wang Lv5

为什么有锁

多线程访问共享资源的时候,避免不了资源竞争而导致数据错乱的问题,所以我们通常为了解决这一问题,都会在访问共享资源之前加锁。
最常用的就是互斥锁,还有很多种不同的锁,比如自旋锁、读写锁、乐观锁等,不同种类的锁自然适用于不同的场景。
如果选择了错误的锁,那么在一些高并发的场景下,可能会降低系统的性能,这样用户体验就会非常差

互斥锁与自旋锁

最底层的两种就是「互斥锁和自旋锁」,很多高级的锁都是基于它们实现:
互斥锁:加锁失败后,线程会释放 CPU ,给其他线程
自旋锁:加锁失败后,线程会忙等待,直到它拿到锁(会死锁,就是这个原因,不同现在互相等待解锁)

读写锁

读写锁从字面意思我们也可以知道,它由「读锁」和「写锁」两部分构成,如果只读取共享资源用「读锁」加锁,如果要修改共享资源则用「写锁」加锁。
工作原理:
当「写锁」没有被线程持有时,多个线程能够并发地持有读锁,这大大提高了共享资源的访问效率,因为「读锁」是用于读取共享资源的场景,所以多个线程同时持有读锁也不会破坏共享资源的数据。但是,一旦「写锁」被线程持有后,读线程的获取读锁的操作会被阻塞,而且其他写线程的获取写锁的操作也会被阻塞。

乐观锁与悲观锁

互斥锁、自旋锁、读写锁,都是属于悲观锁
悲观锁:多线程同时修改共享资源的概率比较高,很容易出现冲突,所以访问共享资源前,先要上锁
乐观锁:假定冲突的概率很低,它的工作方式是:先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作
放弃后如何重试,这跟业务场景息息相关,虽然重试的成本很高,但是冲突的概率足够低的话,是可以接受的。乐观锁全程没有加锁,所以它也叫无锁编程

总结

并发访问共享资源时,冲突概率可能非常高,所以在访问共享资源前,需要先加悲观锁。
相反的,如果并发访问共享资源时冲突概率非常低,可以使用乐观锁

 Comments