深度解析Java当中的锁机制

Java中的锁机制是多线程编程中控制对共享资源访问的重要手段,用于实现线程间的互斥和协调,确保多线程环境下对共享资源的访问顺序和正确性。以下是几种常见的锁机制介绍:

synchronized锁

  • 原理:基于对象的内置锁(monitor)实现。在进入同步代码块或方法前,线程尝试获取对象的锁,获取成功则可执行同步代码,执行完后自动释放锁。在JVM层面,对象头中有相关标记记录锁的状态等信息。当锁升级为偏向锁、轻量级锁、重量级锁时,通过不同的机制实现线程的同步控制。
  • 示例代码
java 复制代码
public class SynchronizedExample {
    // 共享资源
    private int count = 0;
    // 锁对象
    private Object lock = new Object();

    // 同步方法
    public synchronized void increment1() {
        count++;
    }

    // 同步代码块
    public void increment2() {
        synchronized (lock) {
            count++;
        }
    }
}

ReentrantLock锁

  • 原理:通过内部的AQS实现。AQS维护一个同步队列,当线程获取锁失败时,会被加入到同步队列中等待。线程获取锁时,会尝试通过CAS操作修改AQS中的状态变量state。如果获取成功,则将当前线程设置为独占线程;如果获取失败,则根据公平性等策略决定是否将线程加入同步队列。ReentrantLock可重入是通过在获取锁时判断当前线程是否为已获取锁的线程,若是则增加重入次数。
  • 示例代码
java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    // 共享资源
    private int count = 0;
    // 创建可重入锁
    private ReentrantLock lock = new ReentrantLock();

    public void increment() {
        // 加锁
        lock.lock();
        try {
            count++;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}

ReadWriteLock锁

  • 原理:内部维护了读锁和写锁。读锁允许多个线程同时获取,只要没有写锁被持有;写锁是独占的,在写操作时,不允许其他线程获取读锁或写锁。通过AQS的state变量的高、低16位分别记录读锁和写锁的获取次数等信息,实现读写锁的控制。
  • 示例代码
java 复制代码
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
    // 共享资源
    private int data = 0;
    // 创建读写锁
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    // 读锁
    private ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    // 写锁
    private ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();

    // 读操作
    public void readData() {
        readLock.lock();
        try {
            System.out.println("读取数据:" + data);
        } finally {
            readLock.unlock();
        }
    }

    // 写操作
    public void writeData(int newData) {
        writeLock.lock();
        try {
            data = newData;
            System.out.println("写入数据:" + newData);
        } finally {
            writeLock.unlock();
        }
    }
}

StampedLock锁

  • 原理:使用一个版本号(stamp)来标识数据是否被修改。在进行乐观读时,先获取一个乐观读的stamp,然后读取数据,之后通过validate方法验证stamp,如果验证失败,说明数据在读取过程中被修改了,则需要获取悲观读锁或写锁重新读取或修改数据。
  • 示例代码
java 复制代码
import java.util.concurrent.locks.StampedLock;

public class StampedLockExample {
    // 共享资源
    private int x = 0;
    private int y = 0;
    // 创建StampedLock
    private StampedLock lock = new StampedLock();

    // 写操作
    public void move(int deltaX, int deltaY) {
        long stamp = lock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    // 读操作
    public double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();
        int currentX = x;
        int currentY = y;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}
相关推荐
IT_陈寒1 分钟前
Redis持久化配置漏了这一步,线上数据丢了5小时
前端·人工智能·后端
Moment6 分钟前
太好了!NestJS 12 大版本转向 ESM,新项目默认构建换 Rspack
前端·javascript·后端
名字还没想好☜20 分钟前
Go 用 -race 抓数据竞争:一个偶发崩溃的排查、原理与修复
开发语言·后端·golang·go
学习星球34 分钟前
CodeWhale 深度剖析:从 DeepSeek-TUI 到 40.9K Star 的 Rust 终端编程 Agent
开发语言·后端·rust
Rain的Java大神之路39 分钟前
高并发下的热点账户余额扣减:Redis+Lua脚本实现无锁记账
java·spring boot·redis·后端·spring cloud·缓存·lua
kgduu1 小时前
moduo之http
后端
烂蜻蜓4 小时前
Flask入门教程(二十六):Session API——用户会话状态管理
后端·python·flask
郑州光合科技余经理6 小时前
本地生活服务系统:成品模块和定制接口怎么划界
java·前端·人工智能·后端·系统架构·php·ai编程
朦胧之12 小时前
PostgreSQL 数据库笔记
人工智能·后端
IT_陈寒14 小时前
Redis缓存雪崩把我坑惨了,这次长记性了
前端·人工智能·后端