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 提供的 synchronizedReentrantLock 默认就是可重入锁


二、不可重入锁(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.

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

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