Java concurrency之非公平锁_动力节点Java学院整理

作者:skywang12345 时间:2022-07-31 22:04:37 

获取非公平锁(基于JDK1.7.0_40)

非公平锁和公平锁在获取锁的方法上,流程是一样的;它们的区别主要表现在“尝试获取锁的机制不同”。简单点说,“公平锁”在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待);而“非公平锁”在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁)。 

1. lock()


lock()在ReentrantLock.java的NonfairSync类中实现,它的源码如下:
final void lock() {
 if (compareAndSetState(0, 1))
   setExclusiveOwnerThread(Thread.currentThread());
 else
   acquire(1);
}

说明:

lock()会先通过compareAndSet(0, 1)来判断“锁”是不是空闲状态。是的话,“当前线程”直接获取“锁”;否则的话,调用acquire(1)获取锁。

(01) compareAndSetState()是CAS函数,它的作用是比较并设置当前锁的状态。若锁的状态值为0,则设置锁的状态值为1。

(02) setExclusiveOwnerThread(Thread.currentThread())的作用是,设置“当前线程”为“锁”的持有者。

“公平锁”和“非公平锁”关于lock()的对比

  1. 公平锁   -- 公平锁的lock()函数,会直接调用acquire(1)。

  2. 非公平锁 -- 非公平锁会先判断当前锁的状态是不是空闲,是的话,就不排队,而是直接获取锁。

2. acquire()

acquire()在AQS中实现的,它的源码如下:


public final void acquire(int arg) {
 if (!tryAcquire(arg) &&
   acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
   selfInterrupt();
}

(01) “当前线程”首先通过tryAcquire()尝试获取锁。获取成功的话,直接返回;尝试失败的话,进入到等待队列依次排序,然后获取锁。

(02) “当前线程”尝试失败的情况下,会先通过addWaiter(Node.EXCLUSIVE)来将“当前线程”加入到"CLH队列(非阻塞的FIFO队列)"末尾。

(03) 然后,调用acquireQueued()获取锁。在acquireQueued()中,当前线程会等待它在“CLH队列”中前面的所有线程执行并释放锁之后,才能获取锁并返回。如果“当前线程”在休眠等待过程中被中断过,则调用selfInterrupt()来自己产生一个中断。

“公平锁”和“非公平锁”关于acquire()的对比

公平锁和非公平锁,只有tryAcquire()函数的实现不同;即它们尝试获取锁的机制不同。这就是我们所说的“它们获取锁策略的不同所在之处”!

非公平锁的tryAcquire()在ReentrantLock.java的NonfairSync类中实现,源码如下:


protected final boolean tryAcquire(int acquires) {
 return nonfairTryAcquire(acquires);
}

nonfairTryAcquire()在ReentrantLock.java的Sync类中实现,源码如下:


final boolean nonfairTryAcquire(int acquires) {
 // 获取“当前线程”
 final Thread current = Thread.currentThread();
 // 获取“锁”的状态
 int c = getState();
 // c=0意味着“锁没有被任何线程锁拥有”
 if (c == 0) {
   // 若“锁没有被任何线程锁拥有”,则通过CAS函数设置“锁”的状态为acquires。
   // 同时,设置“当前线程”为锁的持有者。
   if (compareAndSetState(0, acquires)) {
     setExclusiveOwnerThread(current);
     return true;
   }
 }
 else if (current == getExclusiveOwnerThread()) {
   // 如果“锁”的持有者已经是“当前线程”,
   // 则将更新锁的状态。
   int nextc = c + acquires;
   if (nextc < 0) // overflow
     throw new Error("Maximum lock count exceeded");
   setState(nextc);
   return true;
 }
 return false;
}

说明:

根据代码,我们可以分析出,tryAcquire()的作用就是尝试去获取锁。

(01) 如果“锁”没有被任何线程拥有,则通过CAS函数设置“锁”的状态为acquires,同时,设置“当前线程”为锁的持有者,然后返回true。

(02) 如果“锁”的持有者已经是当前线程,则将更新锁的状态即可。

(03) 如果不术语上面的两种情况,则认为尝试失败。

“公平锁”和“非公平锁”关于tryAcquire()的对比
公平锁和非公平锁,它们尝试获取锁的方式不同。

公平锁在尝试获取锁时,即使“锁”没有被任何线程锁持有,它也会判断自己是不是CLH等待队列的表头;是的话,才获取锁。

而非公平锁在尝试获取锁时,如果“锁”没有被任何线程持有,则不管它在CLH队列的何处,它都直接获取锁。

释放非公平锁(基于JDK1.7.0_40)

非公平锁和公平锁在释放锁的方法和策略上是一样的。

总结

公平锁和非公平锁的区别,是在获取锁的机制上的区别。表现在,在尝试获取锁时 —— 公平锁,只有在当前线程是CLH等待队列的表头时,才获取锁;而非公平锁,只要当前锁处于空闲状态,则直接获取锁,而不管CLH等待队列中的顺序。

只有当非公平锁尝试获取锁失败的时候,它才会像公平锁一样,进入CLH等待队列排序等待。

标签:Java,非公平锁
0
投稿

猜你喜欢

  • Java环境下高德地图Api的使用方式

    2022-06-13 06:43:59
  • maven profile自动切换环境参数的2种方法详解

    2022-10-28 09:18:39
  • java判断某个点是否在所画多边形/圆形内

    2022-09-30 23:50:45
  • c#实现md5加密示例

    2023-04-19 16:37:11
  • C# webclient中文乱码问题解决方法

    2022-06-02 16:39:43
  • java关键字final使用方法详解

    2023-11-28 22:38:04
  • Springboot引用外部配置文件的方法步骤

    2022-06-14 13:29:35
  • Java 动态代理的多种实现方式

    2023-11-20 05:23:14
  • Java多线程工具篇BlockingQueue的详解

    2022-07-03 20:47:56
  • 详解springmvc常用5种注解

    2023-03-27 15:15:43
  • 基于XML配置Spring的自动装配过程解析

    2023-02-28 00:42:12
  • Java调用wsdl接口的两种方法(axis和wsimport)

    2023-06-23 14:41:22
  • C#异步编程之async/await详解

    2022-10-08 02:50:07
  • Spring Boot整合Web项目常用功能详解

    2023-06-04 17:14:21
  • 完美解决idea创建文件时,文件不分级展示的情况

    2022-01-01 22:10:19
  • Java中简单实用Quartz概述

    2021-09-09 14:16:30
  • SpringBoot Security前后端分离登录验证的实现

    2023-03-09 10:30:07
  • java实现图书检索系统

    2023-08-18 20:08:50
  • java文件处理工具类详解

    2022-12-19 22:49:59
  • C#中缓存的基本用法总结

    2023-12-06 01:22:45
  • asp之家 软件编程 m.aspxhome.com