深入理解 Java 的锁机制:ReentrantLock 与 synchronized

举报
江南清风起 发表于 2025/03/22 17:50:30 2025/03/22
【摘要】 深入理解 Java 的锁机制:ReentrantLock 与 synchronized在多线程编程中,锁机制是确保数据安全的关键。Java 提供了多种锁机制,其中 ReentrantLock 和 synchronized 是两种常用的实现方式。本文将深入探讨这两种锁机制的原理、使用方法以及它们之间的区别。 一、synchronized 关键字synchronized 是 Java 中的一种...

深入理解 Java 的锁机制:ReentrantLock 与 synchronized

在多线程编程中,锁机制是确保数据安全的关键。Java 提供了多种锁机制,其中 ReentrantLock 和 synchronized 是两种常用的实现方式。本文将深入探讨这两种锁机制的原理、使用方法以及它们之间的区别。

一、synchronized 关键字

synchronized 是 Java 中的一种内置锁机制,用于保证多线程环境下的线程安全。它提供了简单易用的互斥访问控制,确保同一时刻只有一个线程可以执行特定代码块。

1.1 使用场景

synchronized 关键字可以用于以下场景:

  • 修饰实例方法:当一个线程访问某个对象的同步方法时,其他线程对该对象的其他同步方法访问将被阻塞。
  • 修饰静态方法:可以使用 synchronized 关键字修饰静态方法,以实现对整个类的同步访问。
  • 修饰代码块:可以使用 synchronized 关键字修饰代码块,以实现对特定资源的同步访问。

1.2 代码示例

public class SynchronizedExample {
    private int counter;

    // 修饰实例方法
    public synchronized void increment() {
        counter++;
    }

    public synchronized int getCount() {
        return counter;
    }
}

public class SynchronizedStaticExample {
    private static int counter;

    // 修饰静态方法
    public static synchronized void increment() {
        counter++;
    }

    public static synchronized int getCount() {
        return counter;
    }
}

public class SynchronizedBlockExample {
    private int counter;
    private Object lock = new Object();

    // 修饰代码块
    public void increment() {
        synchronized (lock) {
            counter++;
        }
    }

    public int getCount() {
        synchronized (lock) {
            return counter;
        }
    }
}

在上面的示例中,increment() 和 getCount() 方法都使用了 synchronized 关键字。这意味着当一个线程正在执行其中一个方法时,其他线程想要执行另一个方法将被阻塞,直到当前线程执行完毕并释放锁。

二、ReentrantLock 类

ReentrantLock 是 java.util.concurrent.locks 包中的一个类,它提供了比 synchronized 更强大和灵活的锁机制。ReentrantLock 允许程序员更加精确地控制线程的加锁和解锁行为,支持中断请求,以及非阻塞式的尝试获取锁等特性。

2.1 基本使用

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

public class ReentrantLockExample {
    private final Lock lock = new ReentrantLock();
    private int counter;

    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return counter;
        } finally {
            lock.unlock();
        }
    }
}

在上面的示例中,ReentrantLockExample 类中定义了一个 ReentrantLock 对象作为显示锁。increment() 和 getCount() 方法在修改或读取 counter 变量时,均首先获取锁,然后在 finally 语句块中确保无论如何都能释放锁,这是一种最佳实践,确保即使在异常情况下也能正确释放锁。

2.2 高级特性

ReentrantLock 还提供了以下高级特性:

  • 可中断的获取锁:线程在等待获取锁时可以响应中断请求。
  • 尝试非阻塞获取锁:线程尝试获取锁,如果获取不到则立即返回,不会阻塞。
  • 超时获取锁:线程尝试在指定时间内获取锁,若超过设定时间仍无法获取,则不再等待并继续执行后续逻辑。
  • 公平锁设置:可以设置锁为公平锁,按照线程请求锁的顺序分配锁。
ReentrantLock lock = new ReentrantLock();

// 可中断的获取锁
lock.lockInterruptibly();

// 尝试非阻塞获取锁
boolean locked = lock.tryLock();

// 超时获取锁
boolean locked = lock.tryLock(2, TimeUnit.SECONDS);

// 公平锁设置
ReentrantLock fairLock = new ReentrantLock(true);

三、ReentrantLock 与 synchronized 的区别

3.1 功能特性

  • 灵活性:ReentrantLock 提供了更多的功能,如可中断的获取锁、尝试非阻塞获取锁、超时获取锁等,而 synchronized 关键字则相对简单,只能实现基本的同步功能。
  • 锁的获取与释放:ReentrantLock 需要显式地调用 lock() 方法获取锁,调用 unlock() 方法释放锁,而 synchronized 则是由 JVM 自动管理锁的获取和释放。

3.2 性能表现

在某些场景下,ReentrantLock 可能比 synchronized 具有更好的性能,尤其是在需要频繁获取和释放锁的情况下。这是因为 ReentrantLock 允许更细粒度的锁控制,可以减少线程的阻塞时间。

3.3 使用场景

  • 简单同步场景:如果只需要基本的同步功能,synchronized 关键字更为简洁易用。
  • 复杂同步场景:当需要更灵活的锁控制,如可中断的锁、公平锁或是需要在锁上附加额外行为时,ReentrantLock 是更好的选择。

四、总结

ReentrantLock 和 synchronized 都是 Java 中常用的锁机制,各有其特点和适用场景。ReentrantLock 提供了更强大和灵活的锁控制功能,适合复杂的多线程编程场景;而 synchronized 关键字则更为简单易用,适合简单的同步需求。在实际开发中,应根据具体需求选择合适的锁机制,以确保程序的线程安全和高性能。
image.png

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。