​​​​​​​Java公平锁和非公平锁的区别

目录
  • 正文
  • 应用场景
  • 公平和非公平锁代码演示
  • 执行流程分析
    • 公平锁执行流程
    • 非公平锁执行流程
  • 优缺点分析
  • 总结

前言:

从公平的角度来说,Java 中的锁总共可分为两类:公平锁和非公平锁。但公平锁和非公平锁有哪些区别?孰优孰劣呢?在 Java 中的应用场景又有哪些呢?接下来我们一起来看。

正文

公平锁:每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的,最前面的线程总是最先获取到锁。
非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,所有线程会竞争获取锁。

 举个例子:公平锁就像开车经过收费站一样,所有的车都会排队等待通过,先来的车先通过,

如下图所示:

通过收费站的顺序也是先来先到,分别是张三、李四、王五,这种情况就是公平锁。 而非公平锁相当于,来了一个强行加塞的老司机,它不会准守排队规则,来了之后就会试图强行加塞,如果加塞成功就顺利通过,当然也有可能加塞失败,如果失败就乖乖去后面排队,这种情况就是非公平锁。

应用场景

在 Java 语言中,锁 synchronized 和 ReentrantLock 默认都是非公平锁,当然我们在创建 ReentrantLock 时,可以手动指定其为公平锁,但 synchronized 只能为非公平锁

 ReentrantLock 默认为非公平锁可以在它的源码实现中得到验证,如下源码所示: 

 当使用 new ReentrantLock(true) 时,可以创建公平锁,如下源码所示: 

公平和非公平锁代码演示

接下来我们使用 ReentrantLock 来演示一下公平锁和非公平锁的执行差异,首先定义一个公平锁,开启 3 个线程,每个线程执行两次加锁和释放锁并打印线程名的操作,

如下代码所示:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockFairTest {
    static Lock lock = new ReentrantLock(true);
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 2; j++) {
                    lock.lock();
                    System.out.println("当前线程:" + Thread.currentThread()
                            .getName());
                    lock.unlock();
                }
            }).start();
        }
    }
}

以上程序的执行结果如下图所示: 

 接下来我们使用非公平锁来执行上面的代码,具体实现如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockFairTest {
    static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 2; j++) {
                    lock.lock();
                    System.out.println("当前线程:" + Thread.currentThread()
                            .getName());
                    lock.unlock();
                }
            }).start();
        }
    }
}

以上程序的执行结果如下图所示: 

从上述结果可以看出,使用公平锁线程获取锁的顺序是:A -> B -> C -> A -> B -> C,也就是按顺序获取锁。而非公平锁,获取锁的顺序是 A -> A -> B -> B -> C -> C,原因是所有线程都争抢锁时,因为当前执行线程处于活跃状态,其他线程属于等待状态(还需要被唤醒),所以当前线程总是会先获取到锁,所以最终获取锁的顺序是:A -> A -> B -> B -> C -> C。

执行流程分析

公平锁执行流程

获取锁时,先将线程自己添加到等待队列的队尾并休眠,当某线程用完锁之后,会去唤醒等待队列中队首的线程尝试去获取锁,锁的使用顺序也就是队列中的先后顺序,在整个过程中,线程会从运行状态切换到休眠状态,再从休眠状态恢复成运行状态,但线程每次休眠和恢复都需要从用户态转换成内核态,而这个状态的转换是比较慢的,所以公平锁的执行速度会比较慢。

非公平锁执行流程

当线程获取锁时,会先通过 CAS 尝试获取锁,如果获取成功就直接拥有锁,如果获取锁失败才会进入等待队列,等待下次尝试获取锁。这样做的好处是,获取锁不用遵循先到先得的规则,从而避免了线程休眠和恢复的操作,这样就加速了程序的执行效率。

公平锁和非公平锁的性能测试结果如下:

从上述结果可以看出,使用非公平锁的吞吐率(单位时间内成功获取锁的平均速率)要比公平锁高很多。

优缺点分析

公平锁的优点是按序平均分配锁资源,不会出现线程饿死的情况,它的缺点是按序唤醒线程的开销大,执行性能不高。 非公平锁的优点是执行效率高,谁先获取到锁,锁就属于谁,不会“按资排辈”以及顺序唤醒,但缺点是资源分配随机性强,可能会出现线程饿死的情况。

总结

在 Java 语言中,锁的默认实现都是非公平锁,原因是非公平锁的效率更高,使用 ReentrantLock 可以手动指定其为公平锁。非公平锁注重的是性能,而公平锁注重的是锁资源的平均分配,所以我们要选择合适的场景来应用二者。

到此这篇关于Java公平锁和非公平锁的区别的文章就介绍到这了,更多相关Java 锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java 线程公平锁与非公平锁详解及实例代码

    java 线程公平锁与非公平锁详解 在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync.公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的:而非公平锁是允许插队的. 默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好.因为从线程进入了RUNNABLE状态,可以执行开始,到实际线程执行是要比较久的时间的.而且,在一个锁释放之后,其

  • ​​​​​​​Java公平锁和非公平锁的区别

    目录 正文 应用场景 公平和非公平锁代码演示 执行流程分析 公平锁执行流程 非公平锁执行流程 优缺点分析 总结 前言: 从公平的角度来说,Java 中的锁总共可分为两类:公平锁和非公平锁.但公平锁和非公平锁有哪些区别?孰优孰劣呢?在 Java 中的应用场景又有哪些呢?接下来我们一起来看. 正文 公平锁:每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的,最前面的线程总是最先获取到锁.非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则,所有线程会竞争获取锁.  举个例子:公平锁就像

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

    目录 概述 RenentrantLock原理概述 非公平锁实现 演示 加锁原理 释放锁原理 公平锁实现 演示 原理实现 总结 概述 ReentrantLock是Java并发中十分常用的一个类,具备类似synchronized锁的作用.但是相比synchronized, 它具备更强的能力,同时支持公平锁和非公平锁. 公平锁: 指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁. 非公平锁: 多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队

  • Java线程公平锁和非公平锁的差异讲解

    公平锁,顾名思义,它是公平的,可以保证获取锁的线程按照先来后到的顺序,获取到锁. 非公平锁,顾名思义,各个线程获取到锁的顺序,不一定和它们申请的先后顺序一致,有可能后来的线程,反而先获取到了锁. 在实现上,公平锁在进行lock时,首先会进行tryAcquire()操作.在tryAcquire中,会判断等待队列中是否已经有别的线程在等待了.如果队列中已经有别的线程了,则tryAcquire失败,则将自己加入队列.如果队列中没有别的线程,则进行获取锁的操作. /** * Fair version o

  • ReentrantLock源码详解--公平锁、非公平锁

    问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 Reentrant = Re + entrant,Re是重复.又.再的意思,entrant是enter的名词或者形容词形式,翻译为进入者或者可进入的,所以Reentrant翻译为可重复进入的.可再次进入的,因此ReentrantLock翻译为重入锁或者再入锁. 重入锁,是指一个线程获取锁之后再尝试获

  • Java循环队列与非循环队列的区别总结

    非循环循环队列 判满:(rear+1) % maxsize == front 判空:front == rear 队列元素个数:rear = (rear + maxsize - front) % maxsize front指针移动方式:front = (front + 1) % maxsize rear指针移动方式:rear= (rear+ 1) % maxsize import java.awt.Font; import java.util.Scanner; import javax.manag

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

    获取非公平锁(基于JDK1.7.0_40) 非公平锁和公平锁在获取锁的方法上,流程是一样的:它们的区别主要表现在"尝试获取锁的机制不同".简单点说,"公平锁"在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而"非公平锁"在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁). 1. lock() lock()在ReentrantLock.java的NonfairSync类

  • java底层AQS实现类kReentrantLock锁的构成及源码解析

    目录 引导语 1.类注释 2.类结构 3.构造器 4.Sync同步器 4.1.nonfairTryAcquire 4.2.tryRelease 5.FairSync公平锁 6.NonfairSync非公平锁 7.如何串起来 7.1lock加锁 7.2tryLock尝试加锁 7.3unlock释放锁 7.4Condition 8.总结 引导语 本章的描述思路是先描述清楚 ReentrantLock 的构成组件,然后使用加锁和释放锁的方法把这些组件串起来. 1.类注释 ReentrantLock 中

  • java非公平锁知识点实例详解

    1.非公平锁不能保证锁的获取是按照请求锁的顺序进行的.这可能会导致某个或某些线程永远得不到锁. 2.CPU唤醒线程的费用可以降低,整体吞吐效率会很高.但是可能会有线程长时间甚至永远得不到锁,导致饿死. 实例 /** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 731615356378282

  • Redis分布式非公平锁的使用

    目录 前言 redis分布式锁第一版 redis分布式锁第二版 redis分布式锁第三版 redis分布式锁最终版 前言 看了很多博客,和资料,这里只针对redis做分布式锁做一下深入探讨,希望对你们有帮助.网上提供了很多分布式锁的操作,这里逐一举例然后评论优缺点及改进方案,希望这样子能让当家更好的理解redis分布式锁. redis分布式锁第一版 大家应该都知道Redis做分布式锁无非就是INCR命令或者是SetNx命令,这里我们采用setnx命令. 操作:setnx key 如果操作成功则代

随机推荐