Java并发编程:synchronized与Lock锁机制详解

Leo
Leo
发布时间:2026-10-16| 4人浏览过
原创

Java中的锁机制是保证多线程并发访问共享资源安全性的核心手段。本文深入解析了内置的关键字与显式的Lock接口,涵盖其底层原理、具体用法及在公平锁、可重入锁和读写锁场景下的差异与选择策略。

synchronized关键字的使用与原理

是Java语言内置的关键字,用于实现对代码块或方法的同步控制。其核心目标是确保在同一时刻只有一个线程可以执行被锁定的代码块或方法,从而避免因多个线程同时对共享资源进行操作而引发的数据不一致问题。

工作原理

锁的实现依赖于Java对象头中的Mark Word(标记字段)。每个Java对象都拥有这样一个Mark Word,用于存储对象的元数据信息。当线程尝试获取锁时,JVM会检查对象头中的标志位;若获取成功,则将其设置为锁定状态;其他线程发现锁已被占用,便会进入等待队列,直到锁被释放。

基本用法

主要应用于两种场景:同步代码块和方法同步。在使用时需注意,方法同步仅对同一对象实例有效。

同步代码块

通过在特定代码块外包裹并指定锁对象,可以精确控制同步范围:

synchronized (lockObject) {
    // 需要同步的代码块
}

其中lockObject必须是引用类型对象。线程进入时会尝试获取该对象的监视器锁,若持有者不为当前线程,则阻塞等待。

同步方法

直接在方法签名上添加修饰符,适用于对整个方法进行原子化控制的场景:

public synchronized void method() {
    // 需要同步的逻辑
}

对于实例方法,锁对象默认为this;对于静态方法,锁对象为该类对应的Class对象。

实战示例:计数器

以下代码展示了如何使用实现线程安全的计数器类:

public class Counter {
    private int count;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }

    public int getCount() {
        return count;
    }
}

在此例中,所有涉及count修改的方法均被同步,确保了增量和减量操作的原子性。

Lock接口的高级控制能力

java.util.concurrent.locks.Lock接口提供了比更灵活的锁定方式。它不仅支持更精细的控制策略(如超时获取、响应中断),还允许实现自定义的锁行为,适用于复杂的并发控制需求。

标准使用模式

由于手动管理锁的生命周期,必须严格遵循“获取-执行-释放”的模式,通常在finally块中释放锁以防止死锁:

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

public class LockCounter {
    private int count;
    private final Lock lock = new ReentrantLock();

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

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

此实现使用了ReentrantLock,它是Lock接口的最常见实现。相比,它要求开发者显式处理异常导致的锁未释放风险。

关键特性对比

1. 公平锁与非公平锁

ReentrantLock支持两种模式:

  • 公平锁:按线程请求锁的时间顺序分配。优点是所有线程都有平等机会,缺点是吞吐量较低,因为存在大量的上下文切换开销。
  • 非公平锁(默认):新获取锁的线程可能与刚释放锁的线程竞争。虽然可能导致部分线程饥饿,但通常性能更高,减少了等待时间。

2. 可重入性

无论是还是ReentrantLock,都支持可重入。即同一线程可以多次获取同一把锁而不会发生死锁。这对于包含嵌套同步调用的方法非常关键,能简化代码逻辑并提高可读性。

3. 高级操作方法

Lock接口提供了丰富的方法:

  • tryLock():尝试获取锁,若失败则立即返回false,不阻塞线程。
  • lockInterruptibly():获取锁时可响应中断,避免线程在无限等待中陷入僵局。
  • condition.await()/signal():配合Condition对象实现更灵活的通知机制,替代wait/notify。

读写锁的应用场景

在读多写少的场景下,普通互斥锁会限制并发性能。ReadWriteLock接口实现了读写分离,允许多个读操作同时进行,但写操作独占。

实现与使用

ReentrantReadWriteLock是最常用的实现类:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyReadWriteLock {
    private int value;
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();

    public int getValue() {
        readLock.lock();
        try {
            return value;
        } finally {
            readLock.unlock();
        }
    }

    public void setValue(int value) {
        writeLock.lock();
        try {
            this.value = value;
        } finally {
            writeLock.unlock();
        }
    }
}

在这种模式下,只要没有写操作在进行,多个线程可以同时调用getValue(),显著提升了读取吞吐率。写操作则完全互斥,确保数据一致性。

总结

选择哪种锁机制取决于具体的业务需求:语法简洁、不易出错,适用于简单的同步场景;Lock接口则提供更强的灵活性、超时控制和中断响应能力,适合构建复杂的高并发系统。在实际开发中,务必注意锁的粒度控制、释放时机以及潜在的死锁风险,以确保系统的稳定性和高性能。

以上就是Java中synchronized与Lock锁机制的详细内容,更多关于Java并发编程的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。