首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >公平锁与非公平锁:原理、特点及应用场景解析

公平锁与非公平锁:原理、特点及应用场景解析

原创
作者头像
一杯茶Ja
发布2024-12-02 20:54:37
发布2024-12-02 20:54:37
9600
举报

在开始之前,推荐大家阅读一篇文章《Spring Web:深度解析与实战应用》https://cloud.tencent.com/developer/article/2472928,该文章深度解析了 Spring Web 模块,涵盖功能、原理、业务应用及实战示例与优缺点,有兴趣的朋友可以去了解下。

前言

在多线程编程的世界里,锁是保障共享资源安全访问的重要机制。而公平锁和非公平锁作为两种常见的锁实现方式,它们有着各自独特的性质与应用场景。本文将深入探讨公平锁和非公平锁的相关内容,帮助读者更好地理解并在实际开发中合理选用它们。

一、公平锁(Fair Lock)

(一)基本原理

公平锁遵循先来先服务的原则,多个线程在竞争锁资源时,按照请求锁的先后顺序依次获得锁的使用权。就好比人们在排队购买火车票,谁先到售票窗口排队,谁就先被服务,不会出现后来者插队先拿到票的情况。当一个线程释放锁之后,处于等待队列头部的线程会被唤醒并获取锁,保证了每个线程获取锁的机会是公平的,不会出现某个线程长时间 “饥饿” 等待锁的现象。

(二)代码示例(以 Java 中的ReentrantLock为例)

在 Java 中,我们可以通过ReentrantLock类来创建公平锁,示例代码如下:

代码语言:java
复制
import java.util.concurrent.locks.ReentrantLock;

public class FairLockExample {
    public static void main(String[] args) {
        // 创建公平锁
        ReentrantLock fairLock = new ReentrantLock(true); 

        Thread thread1 = new Thread(() -> {
            try {
                fairLock.lock();
                System.out.println("线程1获取到锁");
                // 模拟业务操作,这里简单睡眠一下
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                fairLock.unlock();
                System.out.println("线程1释放了锁");
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                fairLock.lock();
                System.out.println("线程2获取到锁");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                fairLock.unlock();
                System.out.println("线程2释放了锁");
            }
        });

        thread1.start();
        thread2.start();
    }
}

在上述代码中,ReentrantLock的构造函数传入true表示创建公平锁。线程 1 先启动去获取锁,在它持有锁并执行完业务操作释放锁之后,线程 2 才有机会获取到锁,严格按照请求顺序来分配锁资源。

(三)特点

  • 公平性保障:确保每个线程都能按照请求顺序获得锁,避免线程 “饿死” 情况,使资源分配更加公平合理,符合我们常规的排队等待逻辑,在对公平性要求较高的场景中非常适用。
  • 性能开销:由于需要维护一个有序的等待队列,每次有线程释放锁时,都要按照队列顺序唤醒下一个等待线程,这涉及到更多的队列操作和线程调度开销,相对来说在高并发场景下性能可能会稍差一些。

二、非公平锁(Unfair Lock)

(一)基本原理

非公平锁在多个线程竞争锁资源时,不会按照请求的先后顺序来分配锁,而是允许新到来的线程去 “抢占” 已经可以获取的锁,只要锁当前处于可用状态。就好像一群人在等公交车,虽然有些人已经等了一会儿,但车一来,后面刚到的人也有可能直接挤上车抢到座位,不需要严格按照先来后到的顺序。当一个线程释放锁后,新请求锁的线程和处于等待队列中的线程都有机会竞争获取锁,谁先抢到就是谁的。

(二)代码示例(同样以 Java 中的ReentrantLock为例)

以下是使用ReentrantLock创建非公平锁的示例代码:

代码语言:java
复制
import java.util.concurrent.locks.ReentrantLock;

public class UnfairLockExample {
    public static void main(String[] args) {
        // 创建非公平锁
        ReentrantLock unfairLock = new ReentrantLock(false); 

        Thread thread1 = new Thread(() -> {
            try {
                unfairLock.lock();
                System.out.println("线程1获取到锁");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unfairLock.unlock();
                System.out.println("线程1释放了锁");
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                unfairLock.lock();
                System.out.println("线程2获取到锁");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                unfairLock.unlock();
                System.out.println("线程2释放了锁");
            }
        });

        thread1.start();
        thread2.start();
    }
}

这里ReentrantLock构造函数传入false(其实不传默认就是非公平锁),线程 2 有可能在线程 1 还没释放锁之前就尝试去获取锁,在 1 释放锁的瞬间,2 也有机会直接获取到锁,而不一定是等待队列中的线程先获取。

(三)特点

  • 高效性(在一定程度上) :减少了维护等待队列的开销,不需要严格按照顺序唤醒等待线程,新到来的线程可以直接尝试获取锁,在高并发场景下,线程获取锁的机会更多、更频繁,整体上可能会有更高的吞吐量,因为避免了一些队列操作的延迟,使得锁能更快地被再次利用起来。
  • 可能导致线程 “饥饿” :由于不遵循先来后到原则,存在某个线程一直抢不到锁,长时间处于等待状态的可能性,也就是出现 “饥饿” 现象,不过在实际大多数应用场景中,如果线程的优先级不是差异特别大,这种 “饥饿” 情况出现的概率相对较小。

三、应用场景对比

(一)公平锁的应用场景

  • 资源分配要求严格公平的情况:例如在操作系统的进程调度中,对于一些需要保证每个进程都能按照请求顺序获得 CPU 时间片等资源的场景,公平锁机制可以确保公平性,防止某个进程长时间无法得到执行机会。
  • 对响应顺序有严格要求的业务场景:比如在一个电商系统中,用户下单的请求处理,如果希望按照用户提交订单的先后顺序来处理,使用公平锁可以保证先下单的用户的请求先被处理,避免出现后下单的用户请求反而先完成处理这种违背业务逻辑的情况。

(二)非公平锁的应用场景

  • 高并发、追求高效吞吐量的场景:像数据库连接池,在大量线程频繁请求获取数据库连接的情况下,使用非公平锁能让线程更快地获取到连接并执行数据库操作,减少线程等待队列的操作开销,提高整体的并发处理能力和系统吞吐量。
  • 线程 “饥饿” 问题影响较小的场景:在普通的 Web 应用中,对于一些非关键业务的资源访问控制,例如缓存数据的更新操作,偶尔某个线程多等待一会儿获取锁影响不大,此时非公平锁的高效优势就可以更好地体现出来。

总结

总之,公平锁和非公平锁各有优劣,在实际的多线程编程中,需要根据具体的业务需求、对公平性的要求以及性能考量等多方面因素,来选择合适的锁机制,以实现高效且可靠的多线程并发操作。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、公平锁(Fair Lock)
    • (一)基本原理
    • (二)代码示例(以 Java 中的ReentrantLock为例)
    • (三)特点
  • 二、非公平锁(Unfair Lock)
    • (一)基本原理
    • (二)代码示例(同样以 Java 中的ReentrantLock为例)
    • (三)特点
  • 三、应用场景对比
    • (一)公平锁的应用场景
    • (二)非公平锁的应用场景
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档