开发场景的各种锁


? 乐观锁

分为三个阶段:数据读取、写入校验、数据写入。

假设数据一般情况下不会造成冲突,只有在数据进行提交更新时,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回错误信息,让用户决定如何去做。fail-fast 机制。

? 悲观锁

正如其名,它指对数据被外界(可能是本机的其他事务,也可能是来自其它服务器的事务处理)的修改持保守态度。在整个数据处理过程中,将数据处于锁定状态。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是长事务而言,这样的开销往往无法承受。

? 分布式锁

分布式集群中,对锁接口 QPS 性能要求很高,单台服务器满足不了要求,可以考虑将锁服务部署在独立的分布式系统中,比如借助分布式缓存来实现。

? 可重入锁

可重入锁,也叫做递归锁,是指在同一个线程在调外层方法获取锁的时候,再进入内层方法会自动获取锁。ReentrantLocksynchronized 都是可重入锁。可重入锁的一个好处是可一定程度避免死锁。

? 自旋锁

自旋锁是采用让当前线程不停地在循环体内执行,当循环的条件被其他线程改变时才能进入临界区。

自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不断增加时,性能下降明显,因为每个线程都需要执行,会占用 CPU 时间片。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

? 独享锁

独享锁是指该锁一次只能被一个线程所持有。

ReentrantLockSynchronized 都是独享锁。

? 共享锁

共享锁是指该锁可被多个线程所持有。ReentrantReadWriteLock,其读锁是共享锁,其写锁是独享锁。读锁的共享锁可保证并发读是非常高效的,读写、写读、写写的过程是互斥的。独享锁与共享锁也是通过 AQS(AbstractQueuedSynchronizer)来实现的,通过实现不同的方法,来实现独享或者共享。

? 互斥锁

独享锁/共享锁就是一种广义的说法,互斥锁/读写锁指具体的实现。

互斥锁在 Java 中的具体实现就是 ReentrantLock

? 读写锁

读写锁在 Java 中的具体实现就是 ReentrantReadWriteLock

? 阻塞锁

阻塞锁,可以说是让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间) 时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程,通过竞争,进入运行状态。

? 公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁

? 非公平锁

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。

可能造成优先级反转或者饥饿现象。对于 Java ReentrantLock 而言,通过构造函数 ReentrantLock(boolean fair) 指定该锁是否是公平锁,默认是非公平锁。

非公平锁的优点在于吞吐量比公平锁大。对于 Synchronized 而言,也是一种非公平锁。

? 分段锁

分段锁其实是一种锁的设计,目的是细化锁的粒度,并不是具体的一种锁,对于 ConcurrentHashMap 而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

? 对象锁

一个线程可以多次对同一个对象上锁。对于每一个对象,java 虚拟机维护一个加锁计数器,线程每获得一次该对象,计数器就加1,每释放一次,计数器就减 1,当计数器值为0时,锁就被完全释放了。

? 类锁

synchronized 修饰静态方法或者同步代码块的 synchronized (类.class),线程想要执行对应同步代码,需要获得类锁。

? 信号量

Semaphore 是用来保护一个或者多个共享资源的访问,Semaphore 内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。

? 条件变量 Condition

条件变量很大一个程度上是为了解决 Object.wait/notify/notifyAll 难以使用的问题。

有人会问,如果一个线程 lock() 后被挂起还没有执行 unlock(),那么另外一个线程就拿不到锁,那么就无法唤醒前一个线程 signal(),这样岂不是“死锁”了?

解释:

进入 lock.lock() 后唯一可能释放锁的操作就是 await()。也就是说 await() 操作实际上就是释放锁,然后挂起线程,一旦条件满足就被唤醒,再次获取锁!

? 行级锁

行级锁是数据库引擎中对记录更新的时候引擎本身上的锁,是数据库引擎的一部分,在数据库引擎更新一条数据的时候,本身就会对记录上锁,这时候即使有多个请求更新,也不会产生脏数据,行级锁的粒度非常细,上锁的时间窗口也最少,只有更新数据记录的那一刻,才会对记录上锁,因此,能大大减少数据库操作的冲突,发生锁冲突的概率最低,并发度也最高。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 学习laravel 进阶之路(一)

>> 下一篇: 【安徽】30W+招聘5年以上PHP大神 包吃包住 丰厚项目奖金!