【JAVA】_002_JAVA锁机制


JAVA锁机制

1.1 悲观锁

  • 悲观锁:顾名思义,在每次拿数据的时候都认为会造成数据的丢失,特别悲观,会直接上锁(如select * from user for update
    • 缺点是只能保持一个链接进行操作,查询量少可使用悲观锁

1.2 乐观锁

  • 乐观锁:顾名思义,很乐观,认为每次查询都不会数据丢失。

    • 原理:利用版本号(数据库字段)+影响行数判断是否为零

1.3 重入锁

  • 重入锁:也叫递归锁,外层函数还是获取该锁之后,内层函数还有获取该锁代码的权限。

    • 常见重入锁:在Java环境下ReentrantLock和synchronized都是可重入锁

      Lock lock = new ReentrantLock();

    • 1.3.1 锁的获取释放过程如下:

      1、线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是则再次获取成功。
      2、锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数器等于0时表示锁已经成功释放了。

      • 分为公平锁和非公平锁:
        • 公平锁:

      ​ 对先发起请求的线程即等待最久的线程优先满足,获取锁是顺序的,符合FIFO原则,不会产生线程饥饿;获取锁调用tryAcquire方法,与非公平锁不一样的地方在于判断条件多了hasQueuedPredecessors()方法,这个方法判断队列中是否有其他节点,如果队列中还有其他节点,但是head后面还没关联节点 / 或者队列中head节点的后继节点关联的线程不是当前线程,如果是返回true,则表示有线程比当前线程更早地请求获取锁,因为要等待前驱节点获取并释放锁后才嫩继续获取到锁。

      • 非公平锁(默认的):

      ​ 获取是使用nonfairTryAcquire方法,只要CAS设置同步状态成功,则当前线程获取了锁。
      非公平锁比公平锁效率更高,因为公平锁为了保证公平性会去切换线程导致上下文切换,存在额外的开销,所以非公平锁性能更好(所以作为默认的实现方式),保证了更大的吞吐量,但是可能会产生线程饥饿。

      1.3.2 重入锁的底层实现:

      锁底层大多方法是使用AQS来实现的。
      下面我们看下ReentrantLock的构造函数

      public ReentrantLock() {
          //默认实现是以非公平锁实现的
          sync = new NonfairSync();
      }
      

      从上面这个代码可以看出,我们要分析两个东西:一个是sync,一个是NofairSync(非公平锁):

      1
      2
      3
      4
      5
      public class ReentrantLock implements Lock, java.io.Serializable {
      //我们刚刚要找的sync字段
      private final Sync sync;
      //Sync继承了AbstractQueuedSynchronizer
      abstract static class Sync extends AbstractQueuedSynchronizer {
      • sync分析:从上面源码可以看出sync是ReentrantLock内的属性,而且Sync是ReentrantLock的内部类,并且继承了AbstractQueuedSynchronizer,这个就是我们常常说的AQS,再进入AQS类看下:
      abstract class AbstractQueuedSynchronizer
      1
      2
      extends AbstractOwnableSynchronizer
      implements java.io.Serializable {

      从上面源码可以看出AbstractQueuedSynchronizer继承AbstractOwnableSynchronizer,也就是AQS继承AOS(后面都用AQS代表AbstractQueuedSynchronizer,AOS代表AbstractOwnableSynchronizer),我们再看看还没分析的NofairSync;

      class ReentrantLock implements Lock, java.io.Serializable {
      1
      2
      //NonfairSync继承Sync
      static final class NonfairSync extends Sync {
      • NofairSync分析:NonfairSync也是ReentrantLock的内部类,并且继承Sync,难怪刚刚new NonfairSync()可以直接赋值给sync我们再看下ReentrantLock类的结构:

      到这里我们可以总结下:
      1.ReentrantLock下面有三个内部类:Sync,NonfairSync,FairSync
      2.AQS继承AOS
      2.Sync继承AQS
      3.NonfairSync(非公平锁)、FairSync(公平锁)分别继承Sync

      那我们可以得出UML图

1.4 读写锁

  • 读写锁:顾名思义是一把锁分为两部分:读锁写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。ReentrantLockReadWriterLock里的readLock和writeLock。

    • 特点读读不互斥、读写互斥、写写互斥

1.5 CAS无锁机制

原子类底层实现保证线程安全,通过CAS无锁机制(天生免疫死锁),效率比有锁机制高。

  • CAS:CAS体系中有三个参数(V,E,N),V表示需要更新的变量,E表示期望值,N表示新值,仅当V=E时,才会将V设置为N,否则什么都不做,最后后返回V的真实值。

1.6 自旋锁

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


文章作者: truly
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 truly !
  目录