锁的分类


悲观锁

  • 定义 对外界修改持保守态度,认为数据很容易会被其他线程修改(悲观),所以数据被处理前先对数据进行枷锁,并在整个数据处理过程中,使数据处于锁定状态。

  • 实现 往往依靠数据库提供的锁机制,即在数据库中,在数据记录操作前先给记录加排它锁。如果获取锁失败,说明数据正在被其他线程修改,当前线程等待或者抛出异常。如果获取锁成功,则对记录进行操作,然后提交事务后释放排它锁。

  • 实例 synchronized关键字就是一种悲观锁

乐观锁

  • 定义 乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新的时候,才会正式对数据冲突与否进行检测

  • 实现 依旧拿数据库的锁进行比较介绍。乐观锁并不会使用数据库提供的锁机制,一般在表中添加 version 字段或者使用业务状态来实现。乐观锁直到提交时才锁定,所以不会产生任何死锁。

  • 实例 CAS原理就是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有一个线程能更新变量的值,而其他线程都会更新失败,失败的线程不会挂起,而是被告知这次竞争失败,可以再次尝试。

公平锁

表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。 把竞争的线程放在一个先进先出的队列上,只要持有锁的线程执行完,唤醒队列的下一个线程去获取锁。

非公平锁

非公平锁则在运行时闯入,不遵循先到先执行的规则。后到的线程可能比前到临界区的线程获取得到锁。

ReentrantLock 提供了公平和非公平锁的实现

独占锁

保证任何时候都只有一个线程能得到锁,ReentrantLock 就是以独占锁方式实现的。

共享锁

则可以同时由多个线程持有,例如 ReadWriteLock 读写锁,它允许一个资源可以被多线程同时进行读操作。

独占锁是一种悲观锁,由于每次访问资源都先加上互斥锁,这限制了并发性,因为读操作并不影响数据一致性,而独占锁只允许在同一时间由一个线程读取数据,其他线程必须等待当前线程释放锁才能进行读取。
共享锁则是一种悲观锁,它放宽了加锁的条件,允许多个线程同时进行操作。

自旋锁

自旋锁则是当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃 CPU 使用权的情况下,多次尝试获取(默认次数是 10 ,可以使用 -XX:PreBlockSpinsh 参数设置该值)。很有可能在后面几次尝试中其他线程已经释放了锁,如果尝试指定次数后仍没有获取到锁则当前线程才会被阻塞挂起。由此看来自旋锁是使用 CPU 时间换取线程阻塞与调度的开销,但是很有可能这些 CPU 时间白白浪费了。




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • 2379. Minimum Recolors to Get K Consecutive Black Blocks
  • 2471. Minimum Number of Operations to Sort a Binary Tree by Level
  • 1387. Sort Integers by The Power Value
  • 2090. K Radius Subarray Averages
  • 2545. Sort the Students by Their Kth Score