锁的分类

  1. 悲观锁和乐观锁

悲观锁: 总是锁住后在进行操作, 例如:lock和synchronized

乐观锁: 总是先认为数据不会发生改变, 例如:cas和git的push

  1. 可重入锁和非可重入锁

不可重入锁:只判断这个锁有没有被锁上,只要被锁上申请锁的线程都会被要求等待。实现简单

可重入锁:不仅判断锁有没有被锁上,还会判断锁是谁锁上的,当就是自己锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。

ps: 设计了加锁次数,所以在解锁的时候,可以确保所有加锁的过程都解锁了,其他线程才能访问。不然没有加锁的参考值,也就不知道什么时候解锁?解锁多少次?才能保证本线程已经访问完临界资源了可以唤醒其他线程访问了。实现相对复杂。

  1. 公平锁和非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。所有的线程都能得到资源,不会饿死在队列中,吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量, 这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

  1. 共享锁和排他锁

以ReentrantReadWriteLock为例, 总结的规则为一写多读.

排队策略: 如果允许读操作插队则会造成饥饿, 所以默认策略为允许写插队, 但是队列第一个需为写操作的时候

升降级: 允许降级不允许升级, 避免死锁发生(多个读锁升级成写锁的情况)

  1. 自旋锁和阻塞锁

自旋锁: 阻塞和唤醒需要操作系统切换CPU状态, 需要耗费处理器时间. 如果代码块内容过于简单, 切换时间可能比执行耗费更长时间.
但是如果占用锁时间过长, 自旋只会白白浪费CPU性能

阻塞锁: 如果没有获得锁直接把线程阻塞直至唤醒.

总结: 自旋起始开销低于阻塞旋, 但是随着时间增长消耗的性能也以线性增长

  1. 可中断锁

synchronized就是不可中断锁, Lock锁的tryLock(带时间)和lockInterruptibly()可以相应线程中断.

posted @ 2020-10-18 11:24  feixiong1688  阅读(162)  评论(0编辑  收藏  举报