JavaEE-synchronized 和 ReentrantLock 之间的区别

synchronized 和 ReentrantLock 之间的区别

Java 中,常用的锁有两种:synchronized(内置锁)和 ReentrantLock(可重入锁)

用法不同

synchronized 可用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用在代码块上。

synchronized 基础使用
使用synchronized修饰代码块

public void method() {
    
    
        // 加锁代码
        synchronized (this) {
    
    
            
        }
    }

ReentrantLock 基础使用
ReentrantLock 在使用之前需要先创建 ReentrantLock 对象,然后使用 lock 方法进行加锁,使用完之后再调用 unlock 方法释放锁,具体使用如下:

import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    
    
    // 创建锁对象
    private final ReentrantLock lock = new ReentrantLock();
    public void method() {
    
    
        // 加锁操作
        lock.lock();
        try {
    
    
            // ...
        } finally {
    
    
            // 释放锁
            lock.unlock();
        }
    }
}

获取锁和释放锁方式不同

synchronized 会自动加锁和释放锁,当进入 synchronized 修饰的代码块之后会自动加锁,当离开 synchronized 的代码段之后会自动释放锁
在这里插入图片描述
ReentrantLock 需要手动加锁和释放锁
在这里插入图片描述

锁类型不同

synchronized 属于非公平锁,而 ReentrantLock 既可以是公平锁也可以是非公平锁。默认情况下 ReentrantLock 为非公平锁

响应中断不同

ReentrantLock 可以使用 lockInterruptibly 获取锁并响应中断指令,而 synchronized 不能响应中断,也就是如果发生了死锁,使用 synchronized 会一直等待下去,而使用 ReentrantLock 可以响应中断并释放锁,从而解决死锁的问题

举一个 ReentrantLock 响应中断的示例:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@SuppressWarnings({
    
    "all"})
public class ReentrantLockInterrupt {
    
    
    static Lock lockA  = new ReentrantLock();
    static Lock lockB  = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
    
    
        // 线程1:先获取 lockA 再获取 lockB
        Thread t1 = new Thread(() -> {
    
    
            try {
    
    
              // 先获取 lockA
                lockA.lockInterruptibly();
                // 休眠10毫秒
                TimeUnit.MICROSECONDS.sleep(100);
                // 获取 lockB
                lockB.lockInterruptibly();
            } catch (InterruptedException e) {
    
    
                System.out.println("响应中断指令");
            } finally {
    
    
                // 释放锁
                lockA.unlock();
                lockB.unlock();
                System.out.println("线程 1 执行完成");
            }
        });

        // 线程2:先获取 lockB 再获取 lockA
        Thread t2 = new Thread(() -> {
    
    
            try {
    
    
                // 先获取 LockB
                lockB.lockInterruptibly();
                // 休眠 10 毫秒
                TimeUnit.MILLISECONDS.sleep(100);
                // 获取 LockA
                lockA.lockInterruptibly();
            } catch (InterruptedException e) {
    
    
                System.out.println("响应中断指令");
            } finally {
    
    
                // 释放锁
                lockB.unlock();
                lockA.unlock();
                System.out.println("线程 2 执行完成。");
            }
        });
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(1);
        // 线程1:执行中断
        t1.interrupt();
    }
}

在这里插入图片描述

底层实现不同

synchronized 是 JVM 层面通过监视器(Monitor)实现的,而 ReentrantLock 是通过 AQS(AbstractQueuedSynchronizer)程序级别的 API 实现。
synchronized 通过监视器实现,可通过观察编译后的字节码得出结论,如下图所示:
在这里插入图片描述
其中 monitorenter 表示进入监视器,相当于加锁操作,而 monitorexit 表示退出监视器,相当于释放锁的操作。ReentrantLock 是通过 AQS 实现,可通过观察 ReentrantLock 的源码得出结论,核心实现源码如下:
在这里插入图片描述

小结

synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁,二者的主要区别有以下 5 个:

用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块。
获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。
锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。
响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。
底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。

声明:此篇文章是通过学习synchronized和ReentrantLock的5个区别!而总结的知识!

猜你喜欢

转载自blog.csdn.net/weixin_61341342/article/details/129864293