温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java 中怎么实现公平锁与非公平锁

发布时间:2021-07-02 14:03:43 来源:亿速云 阅读:345 作者:Leah 栏目:云计算
# Java 中怎么实现公平锁与非公平锁 ## 一、锁的公平性概述 在多线程编程中,锁的公平性(Fairness)是指线程获取锁的顺序是否严格按照请求顺序分配。Java中的`ReentrantLock`提供了公平锁与非公平锁两种实现方式: - **公平锁(Fair Lock)**:按照线程请求锁的顺序分配,先到先得 - **非公平锁(Nonfair Lock)**:允许插队,后请求的线程可能先获取锁 ```java // 创建公平锁 ReentrantLock fairLock = new ReentrantLock(true); // 创建非公平锁(默认) ReentrantLock nonfairLock = new ReentrantLock(); 

二、底层实现原理

1. AQS 核心结构

两种锁的实现都基于AbstractQueuedSynchronizer(AQS)框架:

public class ReentrantLock implements Lock { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { // 实现锁的核心逻辑 } // 非公平锁实现 static final class NonfairSync extends Sync { /*...*/ } // 公平锁实现 static final class FairSync extends Sync { /*...*/ } } 

2. 关键差异点

特性 公平锁 非公平锁
获取锁顺序 严格FIFO 允许插队
吞吐量 较低 较高
线程饥饿 不会 可能发生
实现复杂度 较高 较低

三、源码级实现分析

1. 非公平锁实现

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 直接尝试获取锁,不管队列中是否有等待线程 if (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; } 

2. 公平锁实现

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()) { // ...同非公平锁... } return false; } 

四、性能对比测试

1. 测试代码示例

public class LockBenchmark { private static final int THREADS = 10; private static final int CYCLES = 100000; public static void testLock(ReentrantLock lock) { long start = System.currentTimeMillis(); ExecutorService exec = Executors.newFixedThreadPool(THREADS); IntStream.range(0, CYCLES).forEach(i -> { exec.submit(() -> { lock.lock(); try { Thread.sleep(1); } finally { lock.unlock(); } }); }); exec.shutdown(); while(!exec.isTerminated()); System.out.println("Cost: " + (System.currentTimeMillis()-start)); } } 

2. 典型测试结果

锁类型 线程数 操作次数 耗时(ms)
公平锁 10 100,000 3200
非公平锁 10 100,000 1800

五、应用场景建议

适合使用公平锁的场景

  1. 需要保证线程执行顺序的业务逻辑
  2. 临界区执行时间较长的操作
  3. 对延迟敏感的系统

适合使用非公平锁的场景

  1. 高并发、高性能要求的场景
  2. 临界区执行时间极短(<100μs)
  3. 能容忍短暂饥饿的情况

六、扩展知识

1. synchronized的公平性

内置锁synchronized本质上是非公平锁,没有提供公平策略选项

2. 其他公平性实现

// 公平的Semaphore实现 Semaphore fairSemaphore = new Semaphore(1, true); // 公平的读写锁 ReentrantReadWriteLock fairRwLock = new ReentrantReadWriteLock(true); 

七、常见问题解答

Q1: 为什么默认使用非公平锁?

因为在大多数场景下,非公平锁的吞吐量比公平锁高40%-100%

Q2: 公平锁如何避免饥饿问题?

通过维护一个FIFO队列,严格按顺序唤醒等待线程

Q3: 如何选择锁类型?

  1. 优先尝试非公平锁
  2. 当出现明显饥饿问题时改用公平锁
  3. 对顺序有严格要求时使用公平锁

总结

理解公平锁与非公平锁的关键在于: 1. 公平锁通过hasQueuedPredecessors()保证顺序 2. 非公平锁通过”插队”机制提高吞吐 3. 选择时需要权衡公平性和性能 “`

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI