Java进阶篇--可重入锁 & 不可重入锁

目录

可重入锁(ReentrantLock):

不可重入锁(NonReentrantLock):

总结

代码示例


当涉及到线程同步和互斥访问共享资源时,可重入锁和不可重入锁是两种常见的锁机制。

可重入锁(ReentrantLock):

  • 可重入锁是一种支持重进入的锁机制。重进入是指一个线程在持有锁的情况下,可以再次获取相同的锁而不会被阻塞。
  • 可重入锁实现了Lock接口,提供了比内置锁(synchronized关键字)更多的灵活性和功能。
  • 可重入锁允许一个线程反复获得该锁,避免了死锁的发生,同时也提高了代码的简洁性和可读性。
  • 可重入锁支持公平性设置,使得等待时间最长的线程优先获取锁。

不可重入锁(NonReentrantLock):

  • 不可重入锁是一种不支持重进入的锁机制。也就是说,当一个线程获得了不可重入锁之后,如果再次尝试获取锁,就会被阻塞,直到当前持有锁的线程释放锁。
  • 不可重入锁在Java中没有内置的实现,需要通过自定义实现或基于AQS(AbstractQueuedSynchronizer)等基础类来构建。
  • 不可重入锁可能会导致死锁问题,因为如果一个线程在持有锁的情况下又尝试获取同一个锁,就会导致自己无限等待。

总结

可重入锁允许同一线程多次获得锁,而不可重入锁则不支持同一个线程多次获得锁。在大多数情况下,可重入锁是更常用和推荐的选择,因为它提供了更多的功能、灵活性和安全性,同时避免了死锁问题。但在某些特殊情况下,不可重入锁也可能有其应用场景,例如需要强制确保某段代码只能被一个线程执行。

代码示例

以下是一个将可重入锁(ReentrantLock)和不可重入锁(NonReentrantLock)结合在一起的代码示例,

java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class main {
    private static ReentrantLock reentrantLock = new ReentrantLock();  // 创建可重入锁对象
    private static NonReentrantLock nonReentrantLock = new NonReentrantLock();  // 创建不可重入锁对象

    public static void main(String[] args) {
        Thread thread1 = new Thread(new ReentrantTask());
        Thread thread2 = new Thread(new NonReentrantTask());

        thread1.start();
        thread2.start();
    }

    static class ReentrantTask implements Runnable {
        @Override
        public void run() {
            reentrantLock.lock();  // 获取可重入锁

            try {
                System.out.println(Thread.currentThread().getName() + "进入可重入锁临界区");
                criticalSectionWithReentrantLock();
                System.out.println(Thread.currentThread().getName() + "离开可重入锁临界区");
            } finally {
                reentrantLock.unlock();  // 释放可重入锁
            }
        }

        private void criticalSectionWithReentrantLock() {
            reentrantLock.lock();  // 可重入锁允许同一线程多次获得锁

            try {
                // 在可重入锁临界区域执行需要同步的操作
                System.out.println(Thread.currentThread().getName() + "正在执行可重入锁临界区操作");
            } finally {
                reentrantLock.unlock();  // 释放可重入锁
            }
        }
    }

    static class NonReentrantTask implements Runnable {
        @Override
        public void run() {
            nonReentrantLock.lock();  // 获取不可重入锁

            try {
                System.out.println(Thread.currentThread().getName() + "进入不可重入锁临界区");
                criticalSectionWithNonReentrantLock();
                System.out.println(Thread.currentThread().getName() + "离开不可重入锁临界区");
            } finally {
                nonReentrantLock.unlock();  // 释放不可重入锁
            }
        }

        private void criticalSectionWithNonReentrantLock() {
            nonReentrantLock.lock();  // 不可重入锁不允许同一线程多次获得锁

            try {
                // 在不可重入锁临界区域执行需要同步的操作
                System.out.println(Thread.currentThread().getName() + "正在执行不可重入锁临界区操作");
            } finally {
                nonReentrantLock.unlock();  // 释放不可重入锁
            }
        }
    }

    static class NonReentrantLock {
        private boolean isLocked = false;

        public synchronized void lock() {
            while (isLocked) {
                try {
                    wait();  // 等待直到当前线程获取锁
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }

            isLocked = true;
        }

        public synchronized void unlock() {
            isLocked = false;
            notify();  // 唤醒等待的线程
        }
    }
}

在这个示例中,我们创建了一个可重入锁对象 reentrantLock 和一个不可重入锁对象 nonReentrantLock ,并在 ReentrantTask 和 NonReentrantTask 类中分别使用这两种类型的锁来实现线程同步。

ReentrantTask 类使用可重入锁,在 run() 方法中获取可重入锁并进入临界区域执行操作,而且可以在临界区域内再次获得相同的锁。这展示了可重入锁的特性。

NonReentrantTask 类使用不可重入锁,在 run() 方法中获取不可重入锁并进入临界区域执行操作,但是如果尝试再次获取相同的锁,将被阻塞。这展示了不可重入锁的特性。

注意,不可重入锁 NonReentrantLock 是通过自定义类实现的。它使用一个布尔变量 isLocked 来表示当前锁是否被占用,并在 lock() 和 unlock() 方法中使用 synchronized 关键字和 wait()、notify() 方法实现线程同步。

请注意,混合使用可重入锁和不可重入锁可能需要谨慎处理,以避免死锁和其它线程同步问题。

相关推荐
徐小黑ACG22 分钟前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
0白露1 小时前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.2 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
战族狼魂3 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
Tttian6224 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL4 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch5 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
独好紫罗兰5 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法