非公平锁与公平锁的实现

在多线程编程中,锁是一个非常重要的概念。锁用于保护共享资源,防止多个线程同时访问,从而避免数据不一致或损坏。根据锁的分配策略,锁可以分为"公平锁"和"非公平锁"。本文将为你详细介绍如何实现这两种锁。

什么是公平锁与非公平锁?

  • 公平锁:按请求锁的顺序来获取锁的权利,先进先出,保证了线程的公平性。
  • 非公平锁:不保证线程的获取顺序,可能导致某些线程长时间等待。

流程概述

实现公平锁和非公平锁的流程如下表所示:

步骤 动作
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代码实现,包括类图和流程图。理解这两种锁的工作方式是多线程编程中非常重要的一部分,希望通过本文的解释和示例代码,能帮助你更好地掌握锁的使用。若还有其他问题或需要进一步的学习,欢迎随时交流!