非公平锁与公平锁的实现
在多线程编程中,锁是一个非常重要的概念。锁用于保护共享资源,防止多个线程同时访问,从而避免数据不一致或损坏。根据锁的分配策略,锁可以分为"公平锁"和"非公平锁"。本文将为你详细介绍如何实现这两种锁。
什么是公平锁与非公平锁?
- 公平锁:按请求锁的顺序来获取锁的权利,先进先出,保证了线程的公平性。
- 非公平锁:不保证线程的获取顺序,可能导致某些线程长时间等待。
流程概述
实现公平锁和非公平锁的流程如下表所示:
步骤 | 动作 |
---|---|
1 | 创建一个锁类(FairLock / UnfairLock) |
2 | 在类中维护一个队列,用于存储请求锁的线程 |
3 | 实现获取锁的方法 |
4 | 实现释放锁的方法 |
5 | 编写示例程序来测试锁的行为 |
流程图
以下是整体流程图,帮助理解步骤之间的关系。
flowchart TD
A[创建一个锁类] --> B[维护一个队列]
B --> C[实现获取锁的方法]
C --> D[实现释放锁的方法]
D --> E[编写示例程序]
实现步骤
第一步:创建一个锁类
我们可以创建一个抽象的锁类,然后分别实现公平锁和非公平锁。
public abstract class MyLock {
// 维护队列
protected Queue<Thread> waitQueue = new LinkedList<>();
// 获取锁的抽象方法
public abstract void lock();
// 释放锁的抽象方法
public abstract void unlock();
}
这里,我们创建了一个
MyLock
类,定义了两个抽象方法lock()
和unlock()
,以及一个用来存储请求锁的线程的队列。
第二步:实现公平锁
import java.util.Queue;
import java.util.LinkedList;
public class FairLock extends MyLock {
private boolean isLocked = false;
public synchronized void lock() {
Thread currentThread = Thread.currentThread();
waitQueue.add(currentThread); // 将当前线程加入队列
while (isLocked || waitQueue.peek() != currentThread) {
try {
wait(); // 休息等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true; // 获取锁
waitQueue.poll(); // 移除队列头
}
public synchronized void unlock() {
isLocked = false; // 释放锁
notifyAll(); // 唤醒所有等待线程
}
}
这里的
lock()
方法保证了线程的公平性。在获取锁之前,线程将自己添加到队列中,并且只有队列头的线程才能获取锁。unlock()
方法则释放锁并通知其他等待的线程。
第三步:实现非公平锁
public class UnfairLock extends MyLock {
private boolean isLocked = false;
public synchronized void lock() {
while (isLocked) {
try {
wait(); // 休息等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true; // 获取锁
}
public synchronized void unlock() {
isLocked = false; // 释放锁
notify(); // 唤醒一个等待线程
}
}
在非公平锁实现中,
lock()
方法没有使用队列,任何请求锁的线程都可以直接尝试获取锁。其结果是可能导致某些线程长时间得不到锁,而某些线程多次获取锁。
示例程序测试
在实现了公平锁和非公平锁后,我们可以通过示例程序来测试它们的行为。
public class LockTest {
private static final MyLock fairLock = new FairLock();
private static final MyLock unfairLock = new UnfairLock();
public static void main(String[] args) {
testLock(fairLock);
testLock(unfairLock);
}
private static void testLock(MyLock lock) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
lock.lock();
System.out.println(Thread.currentThread().getName() + " acquired lock");
try {
Thread.sleep(1000); // 模拟执行业务
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}).start();
}
}
}
示例程序中,我们创建了5个线程在分别获取公平锁和非公平锁。
testLock()
方法通过lock()
和unlock()
方法验证了锁的行为。
类图
下面是 MyLock
及其子类的类图,帮助你了解其结构和关系。
classDiagram
class MyLock {
+Queue<Thread> waitQueue
+lock()
+unlock()
}
class FairLock {
-boolean isLocked
+lock()
+unlock()
}
class UnfairLock {
-boolean isLocked
+lock()
+unlock()
}
MyLock <|-- FairLock
MyLock <|-- UnfairLock
总结
在本文中,我们详细解释了“公平锁”和“非公平锁”的概念,并提供了Java代码实现,包括类图和流程图。理解这两种锁的工作方式是多线程编程中非常重要的一部分,希望通过本文的解释和示例代码,能帮助你更好地掌握锁的使用。若还有其他问题或需要进一步的学习,欢迎随时交流!