Home 并发编程:可重入锁、不可重入锁、公平锁、非公平锁
Post
Cancel

并发编程:可重入锁、不可重入锁、公平锁、非公平锁

并发编程:可重入锁、不可重入锁、公平锁、非公平锁


一、可重入锁(Reentrant Lock)

概念

同一个线程在外层方法获取锁之后,在进入该锁保护的内层方法时,可以再次获得锁,不会被自己阻塞。

特点:

  • 锁维护一个持有线程的引用和重入计数器
  • 每 lock() 一次,计数 +1,unlock() 一次,计数 -1

Java 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ReentrantExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void outer() {
        lock.lock();
        try {
            System.out.println("Entered outer");
            inner(); // 可重入:这里不会死锁
        } finally {
            lock.unlock();
        }
    }

    public void inner() {
        lock.lock(); // 同一线程再次获得锁
        try {
            System.out.println("Entered inner");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        new ReentrantExample().outer();
    }
}

本质:

JDK 提供的 synchronized 和 ReentrantLock 默认就是可重入锁。


二、不可重入锁(Non-Reentrant Lock)

概念

一个线程再次请求自己持有的锁时会被阻塞,容易导致死锁。

示例(自定义一个不可重入锁):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class NonReentrantLock {
    private boolean isLocked = false;
    private Thread lockedBy = null;

    public synchronized void lock() throws InterruptedException {
        while (isLocked) {
            wait();
        }
        isLocked = true;
        lockedBy = Thread.currentThread();
    }

    public synchronized void unlock() {
        if (Thread.currentThread() == lockedBy) {
            isLocked = false;
            lockedBy = null;
            notify();
        }
    }
}

风险:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
    private final NonReentrantLock lock = new NonReentrantLock();

    public void methodA() throws InterruptedException {
        lock.lock();
        System.out.println("methodA");
        methodB(); // 死锁:同线程再次加锁会阻塞
        lock.unlock();
    }

    public void methodB() throws InterruptedException {
        lock.lock(); // 被阻塞
        System.out.println("methodB");
        lock.unlock();
    }
}

三、公平锁(Fair Lock)

概念

线程获取锁的顺序与它们请求锁的顺序一致,先来先得。

🔍 Java 实现

1
ReentrantLock lock = new ReentrantLock(true); // 公平锁

在这里插入图片描述

特点

  • 所有等待线程会排队
  • 每个线程轮到时才能拿到锁
  • 避免线程饥饿

四、非公平锁(Non-Fair Lock)

概念

线程在请求锁时,可以插队,如果锁空闲就立即获取,不管有没有其他等待线程。

🔍 Java 实现

1
ReentrantLock lock = new ReentrantLock(false); // 非公平锁,默认

在这里插入图片描述

特点

  • 性能高,吞吐量大
  • 可能出现饥饿现象
  • 减少线程上下文切换

五、总结

分类 可重入锁 不可重入锁
定义 同一线程可以重复获取已持有的锁 同一线程重复获取锁会阻塞自己
例子 synchronized, ReentrantLock 自定义锁或极简实现
是否阻塞自身 否 是
死锁风险 小 大
分类 公平锁 非公平锁
加锁顺序 先进先出 插队,谁抢到谁执行
实现方式 ReentrantLock(true) ReentrantLock(false)
性能 较低,避免饥饿 较高,但可能造成饥饿
使用场景 任务调度敏感、公平性重要 性能优先,锁竞争激烈

六、建议使用方式

场景 建议使用锁类型
方法嵌套、自调用 可重入锁(默认即可)
锁竞争不激烈、性能优先 非公平锁(默认)
对延迟和公平性要求高(如限流) 公平锁
自定义锁类 谨慎,需考虑可重入性

weixin.png

公众号名称:怪味Coding
微信扫码关注或搜索公众号名称
This post is licensed under CC BY 4.0 by the author.

并发编程:为什么并发编程容易出问题

并发编程:单核计算机如何通过交替执行实现并发