图解Java ReentrantLock公平锁和非公平锁的实现

图解Java ReentrantLock公平锁和非公平锁的实现攻略

什么是ReentrantLock

ReentrantLock是一个可重入锁,也称为互斥锁,它比Java原生的synchronized更加灵活,支持公平锁和非公平锁,并且可以通过tryLock方法尝试获取锁,给予更好的控制和扩展。

公平锁和非公平锁

公平锁和非公平锁都是指ReentrantLock的获取锁机制。公平锁是按照请求锁的顺序排队,先请求锁的先获得锁,而非公平锁则不保证请求锁的顺序,也就是说后来的线程可能会在先前线程的前面请求到锁。

公平锁实现

公平锁的实现是基于FIFO队列的,通过一个带有head和tail指针的FIFO队列来维护请求锁的顺序,在每个节点都保存了前驱节点的信息,新的线程请求锁时,会将其加入到队列的末尾,且只有当前持有锁的线程释放锁的时候,才唤醒队列中的下一个线程。

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

private static final class FairSync extends Sync {
    // 公平锁使用FIFO队列
    final void lock() {
        acquire(1);
    }
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) { // 如果锁没有被占用
            if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                // 没有等待线程,直接将锁占用,并设置独占线程为当前线程
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 如果该线程之前已经获得了该锁(可重入)
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

非公平锁实现

与公平锁不同,非公平锁的实现没有排序的过程,它总是尝试获取锁,如果失败就进入等待状态,直到成功获取锁或者线程被中断或超时等不可抗力情况发生。

private static final class NonfairSync extends Sync {
    // 非公平锁使用CAS操作直接尝试获取锁
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

示例

下面我们通过两个示例来说明公平锁和非公平锁的区别。

假设有两个线程A和B,并且有一个非公平锁lock。

示例1:使用公平锁

ReentrantLock fairLock = new ReentrantLock(true);
fairLock.lock();
System.out.println("A acquire fairLock");
fairLock.lock();
System.out.println("B acquire fairLock");
fairLock.unlock();
System.out.println("A release fairLock");
fairLock.unlock();
System.out.println("B release fairLock");

输出结果为:

A acquire fairLock
B acquire fairLock
A release fairLock
B release fairLock

公平锁保证线程请求锁的顺序,所以期望的输出结果就是先有A取得锁,再有B取得锁,最后A释放锁,B也随后释放锁。

示例2:使用非公平锁

ReentrantLock nonfairLock = new ReentrantLock();
nonfairLock.lock();
System.out.println("A acquire nonfairLock");
nonfairLock.lock();
System.out.println("B acquire nonfairLock");
nonfairLock.unlock();
System.out.println("A release nonfairLock");
nonfairLock.unlock();
System.out.println("B release nonfairLock");

输出结果为:

A acquire nonfairLock
B acquire nonfairLock
A release nonfairLock
B release nonfairLock

由于非公平锁没有按照请求锁的顺序进行排序,所以线程B也可以在A获得锁后立即尝试获取,成功获得锁,然后先释放锁,线程A再接着去释放锁。

结论

公平锁显然比非公平锁更加公正,能够避免饥饿现象,但是由于需要排序和维护队列,开销会更大,而非公平锁相对更加简单和高效,但是有可能会出现饥饿情况。所以选择哪一种锁取决于实际应用场景和需求。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:图解Java ReentrantLock公平锁和非公平锁的实现 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • ios沙盒简单介绍

    ios沙盒简单介绍 在iOS操作系统中,沙盒是应用程序运行时的一个私有目录,应用程序只能访问自己的沙盒,不能访问其他应用程序的沙盒。 沙盒目录结构如下: Application/ 应用程序包/ 应用程序 其他资源文件 Documents/ 用户文档 Library/ Caches/ 应用缓存 Preferences/ 应用程序的偏好设置 tmp/ 临时文件 …

    其他 2023年3月29日
    00
  • windowsgcc怎么安装

    WindowsGCC怎么安装 如果你需要在Windows系统上使用GCC编译器,可以使用MinGW来安装GCC。这里提供一份详细的安装步骤。 一、下载MinGW 访问官网并下载最新的MinGW版本,目前最新版本为MinGW-8.1.0。 二、安装MinGW 下载完成后,双击运行下载的 exe 文件并按照指示进行安装。在安装过程中,需要对MinGW进行一些设置…

    其他 2023年3月28日
    00
  • Mysql计算字段长度函数之CHAR_LENGTH函数

    当我们在使用 MySQL 数据库时,可能需要使用到字符串的长度,MySQL 提供了多个计算字符串长度的函数。其中一个函数就是 CHAR_LENGTH。以下是 CHAR_LENGTH 函数的详细攻略。 CHAR_LENGTH 函数的概述 CHAR_LENGTH 函数用于计算指定字符串中的字符数量,以 Unicode 字符集中的编码计算。即,如果字符串中包含中文…

    other 2023年6月25日
    00
  • Android Parcelable接口使用方法详解

    首先介绍一下Parcelable接口,它是Android平台内部用于进程间通信(IPC)的一个轻量级序列化框架,相比较于Java自带的Serializable接口,Parcelable接口在性能方面有更好的表现。 一、实现Parcelable接口 要使用Parcelable接口,需要先实现它。具体实现过程如下所示: 1.在实体类中实现Parcelable接口…

    other 2023年6月27日
    00
  • sed总结 mac上要加备份文件名 sort命令和对中文的处理

    sed总结 mac上要加备份文件名 sort命令和对中文的处理 在Mac系统上,常常需要使用sed命令进行文本替换,但是在使用sed命令时需要注意一些注意事项。本文将总结一下使用sed命令时注意的问题,以及如何使用sort命令和对中文进行处理。 添加备份文件名 在使用sed命令替换文件内容时,最好添加备份文件名。这样在修改文件时会生成原始文件的备份,防止出现…

    其他 2023年3月28日
    00
  • adbdevicesunauthorized的解决办法

    adbdevicesunauthorized的解决办法 问题描述 在使用Android设备进行调试时,常常会遇到”adb devices”命令无法识别设备的问题,命令行输出结果为: List of devices attached ???????????? no permissions 这种情况通常是因为设备没有被授权访问电脑所致。 解决办法 1. 授权调试…

    其他 2023年3月29日
    00
  • html提示信息的样式

    HTML提示信息的样式 在网站开发中,常常需要向用户提供一些重要信息,例如错误提示、警告、成功提示等。那么如何用 HTML 语言来呈现这些提示信息呢?本文将介绍几种常用的 HTML 提示信息样式。 1. 普通文本 最简单的提示信息样式就是普通文本,例如: <p>请注意:提交前请检查输入内容是否正确。</p> 这种方式非常简单,但缺点是…

    其他 2023年3月28日
    00
  • vue2.0使用v-for循环制作多级嵌套菜单栏

    Vue 2.0使用v-for循环制作多级嵌套菜单栏攻略 在Vue 2.0中,我们可以使用v-for指令来循环渲染多级嵌套菜单栏。下面是一个详细的攻略,包含两个示例说明。 步骤1:准备数据 首先,我们需要准备一个包含多级嵌套菜单栏数据的数组。每个菜单项都应该包含一个唯一的标识符(id),菜单项的名称(name),以及子菜单项(children)(如果有的话)。…

    other 2023年7月28日
    00
合作推广
合作推广
分享本页
返回顶部