java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java锁的类型

Java中各类锁的类型、核心作用、适用场景示例代码详解

作者:哎呦喂606

这段文章详细介绍了Java中的多种锁机制,包括内置锁synchronized、显式锁ReentrantLock、读写锁ReentrantReadWriteLock及进阶锁StampedLock,分别适用于不同场景,感兴趣的朋友跟随小编一起看看吧

一、基础锁:内置锁(synchronized)

核心特点

synchronized 是Java内置的隐式锁(JVM层面实现),具备可重入性、默认非公平性,锁会自动释放(方法执行完毕/抛出异常时),无需手动管理。

作用

保证多线程环境下的原子性(操作不可分割)、可见性(一个线程修改的变量对其他线程可见)、有序性(禁止指令重排),解决线程安全问题。

使用场景

适用于简单的线程安全场景(如单例、共享变量修改),开发成本低,无需手动释放锁,适合新手或快速实现同步的场景。

示例代码(三种使用方式)

public class SynchronizedLockDemo {
    // 1. 修饰实例方法(锁对象是当前类的实例)
    public synchronized void instanceMethodLock() {
        System.out.println("实例方法锁:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000); // 模拟业务操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    // 2. 修饰静态方法(锁对象是当前类的Class对象)
    public static synchronized void staticMethodLock() {
        System.out.println("静态方法锁:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    // 3. 修饰代码块(手动指定锁对象,灵活性更高)
    public void codeBlockLock() {
        // 锁对象可以是任意非null对象,常用this(实例锁)或类对象(全局锁)
        synchronized (this) {
            System.out.println("代码块锁(实例):" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    public static void main(String[] args) {
        SynchronizedLockDemo demo = new SynchronizedLockDemo();
        // 测试实例方法锁(多线程竞争同一个实例锁)
        new Thread(demo::instanceMethodLock, "线程1").start();
        new Thread(demo::instanceMethodLock, "线程2").start();
        // 测试静态方法锁(多线程竞争类锁)
        new Thread(SynchronizedLockDemo::staticMethodLock, "线程3").start();
        new Thread(SynchronizedLockDemo::staticMethodLock, "线程4").start();
        // 测试代码块锁
        new Thread(demo::codeBlockLock, "线程5").start();
        new Thread(demo::codeBlockLock, "线程6").start();
    }
}

代码解释

二、显式锁:ReentrantLock(可重入锁)

核心特点

实现 java.util.concurrent.locks.Lock 接口,是显式锁(手动加锁/释放锁),支持可重入性,可配置公平锁/非公平锁,还支持中断、超时获取锁、尝试获取锁等高级特性。

作用

synchronized 更灵活,解决 synchronized 无法中断、无法超时、无法尝试获取锁的问题,适合复杂的同步场景。

使用场景

示例代码(核心特性演示)

import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
    // 1. 创建ReentrantLock:参数true为公平锁,false(默认)为非公平锁
    private static final ReentrantLock FAIR_LOCK = new ReentrantLock(true);
    private static final ReentrantLock NON_FAIR_LOCK = new ReentrantLock(false);
    // 演示公平锁(按等待顺序获取锁)
    public static void fairLockDemo() {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                FAIR_LOCK.lock(); // 手动加锁
                try {
                    System.out.println("公平锁获取成功:" + Thread.currentThread().getName());
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    FAIR_LOCK.unlock(); // 必须在finally中释放锁,避免死锁
                }
            }, "公平锁线程-" + i).start();
        }
    }
    // 演示尝试获取锁(非阻塞)
    public static void tryLockDemo() {
        Thread thread1 = new Thread(() -> {
            NON_FAIR_LOCK.lock();
            try {
                System.out.println("线程1持有锁,执行10秒");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                NON_FAIR_LOCK.unlock();
            }
        });
        Thread thread2 = new Thread(() -> {
            boolean isLockAcquired = NON_FAIR_LOCK.tryLock(); // 尝试获取锁,立即返回结果
            if (isLockAcquired) {
                try {
                    System.out.println("线程2获取锁成功");
                } finally {
                    NON_FAIR_LOCK.unlock();
                }
            } else {
                System.out.println("线程2获取锁失败,执行备用逻辑");
            }
        });
        thread1.start();
        Thread.sleep(1000); // 确保线程1先获取锁
        thread2.start();
    }
    // 演示超时获取锁(避免永久阻塞)
    public static void tryLockWithTimeoutDemo() throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            NON_FAIR_LOCK.lock();
            try {
                System.out.println("线程1持有锁,执行3秒");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                NON_FAIR_LOCK.unlock();
            }
        });
        Thread thread2 = new Thread(() -> {
            try {
                // 尝试获取锁,最多等待5秒,超时返回false
                boolean isLockAcquired = NON_FAIR_LOCK.tryLock(5, java.util.concurrent.TimeUnit.SECONDS);
                if (isLockAcquired) {
                    try {
                        System.out.println("线程2超时等待后获取锁成功");
                    } finally {
                        NON_FAIR_LOCK.unlock();
                    }
                } else {
                    System.out.println("线程2超时等待5秒后仍未获取锁");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("线程2获取锁时被中断");
            }
        });
        thread1.start();
        Thread.sleep(100); // 确保线程1先获取锁
        thread2.start();
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println("===== 公平锁演示 =====");
        fairLockDemo();
        Thread.sleep(3000); // 等待公平锁演示完成
        System.out.println("\n===== 尝试获取锁演示 =====");
        tryLockDemo();
        Thread.sleep(11000); // 等待尝试获取锁演示完成
        System.out.println("\n===== 超时获取锁演示 =====");
        tryLockWithTimeoutDemo();
    }
}

代码解释

三、读写锁:ReentrantReadWriteLock

核心特点

基于 Lock 接口,分为读锁(共享锁)写锁(独占锁)

作用

优化读多写少的场景(如缓存、配置读取),解决 synchronized/ReentrantLock 独占锁导致读操作串行执行的性能问题。

使用场景

示例代码

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * 读写锁演示:模拟缓存系统(读多写少)
 */
public class ReadWriteLockDemo {
    // 缓存容器
    private static final Map<String, String> CACHE = new HashMap<>();
    // 读写锁
    private static final ReentrantReadWriteLock RW_LOCK = new ReentrantReadWriteLock();
    // 读锁
    private static final ReentrantReadWriteLock.ReadLock READ_LOCK = RW_LOCK.readLock();
    // 写锁
    private static final ReentrantReadWriteLock.WriteLock WRITE_LOCK = RW_LOCK.writeLock();
    // 从缓存读取数据(读操作,加读锁)
    public static String get(String key) {
        READ_LOCK.lock(); // 加读锁
        try {
            System.out.println(Thread.currentThread().getName() + " 读取缓存:" + key);
            Thread.sleep(500); // 模拟读操作耗时
            return CACHE.get(key);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            READ_LOCK.unlock(); // 释放读锁
        }
    }
    // 向缓存写入数据(写操作,加写锁)
    public static void put(String key, String value) {
        WRITE_LOCK.lock(); // 加写锁
        try {
            System.out.println(Thread.currentThread().getName() + " 写入缓存:" + key + "=" + value);
            Thread.sleep(1000); // 模拟写操作耗时
            CACHE.put(key, value);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            WRITE_LOCK.unlock(); // 释放写锁
        }
    }
    public static void main(String[] args) {
        // 1. 模拟多线程读缓存(读锁共享,并行执行)
        for (int i = 0; i < 5; i++) {
            new Thread(() -> get("user:1001"), "读线程-" + i).start();
        }
        // 2. 模拟写缓存(写锁独占,串行执行)
        new Thread(() -> put("user:1001", "张三"), "写线程-1").start();
        new Thread(() -> put("user:1002", "李四"), "写线程-2").start();
        // 3. 模拟读-写互斥(读线程等待写线程释放锁)
        new Thread(() -> get("user:1002"), "读线程-5").start();
    }
}

代码解释

四、设计思想:乐观锁 vs 悲观锁

这是锁的设计思想,而非具体的锁实现,Java中很多锁基于这两种思想实现。

1. 悲观锁

核心特点

认为每次操作都会有线程竞争,先加锁再操作,确保操作过程中无其他线程干扰。

作用

解决高竞争场景下的线程安全问题,牺牲性能换安全。

使用场景

线程竞争激烈、写操作频繁的场景(如库存扣减、转账)。

示例代码(synchronized实现悲观锁)

public class PessimisticLockDemo {
    private int count = 0;
    // 悲观锁:先加锁再操作
    public synchronized void increment() {
        count++;
        System.out.println(Thread.currentThread().getName() + " 执行自增,count=" + count);
    }
    public static void main(String[] args) {
        PessimisticLockDemo demo = new PessimisticLockDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(demo::increment, "线程-" + i).start();
        }
    }

2. 乐观锁

核心特点

认为每次操作无竞争,不加锁,仅在更新时通过CAS(Compare And Swap) 检查数据是否被修改:

作用

无锁竞争时性能极高,牺牲少量安全性(极端场景可能ABA问题)换性能。

使用场景

线程竞争小、读多写少的场景(如计数器、库存扣减优化)。

示例代码(AtomicInteger实现CAS乐观锁)

import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockDemo {
    // AtomicInteger底层通过CAS实现乐观锁
    private static final AtomicInteger COUNT = new AtomicInteger(0);
    // 乐观锁:无锁操作,CAS更新
    public static void increment() {
        // CAS自增:预期值为当前值,更新为当前值+1,失败则重试
        int oldValue;
        int newValue;
        do {
            oldValue = COUNT.get(); // 获取当前值
            newValue = oldValue + 1; // 计算新值
            // CAS尝试更新:如果当前值还是oldValue,就更新为newValue,返回true;否则返回false
        } while (!COUNT.compareAndSet(oldValue, newValue));
        System.out.println(Thread.currentThread().getName() + " 执行自增,count=" + newValue);
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(OptimisticLockDemo::increment, "线程-" + i).start();
        }
    }
}

代码解释

五、进阶锁:StampedLock(Java8新增)

核心特点

读写锁的增强版,支持三种模式:

作用

优化读极多、写极少的场景,乐观读无锁竞争,性能远超ReentrantReadWriteLock。

使用场景

超高并发的读场景(如高频访问的商品详情、用户信息)。

示例代码

import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
    private double x = 0.0, y = 0.0;
    private final StampedLock stampedLock = new StampedLock();
    // 写操作:获取写锁
    public void move(double deltaX, double deltaY) {
        long stamp = stampedLock.writeLock(); // 获取写锁,返回版本戳
        try {
            x += deltaX;
            y += deltaY;
            System.out.println("写操作:x=" + x + ", y=" + y);
        } finally {
            stampedLock.unlockWrite(stamp); // 释放写锁
        }
    }
    // 乐观读操作:无锁,仅检查版本戳
    public double distanceFromOrigin() {
        long stamp = stampedLock.tryOptimisticRead(); // 获取乐观读戳
        double currentX = x, currentY = y; // 读取数据
        // 检查乐观读期间是否有写操作(版本戳是否有效)
        if (!stampedLock.validate(stamp)) {
            // 有写操作,升级为悲观读锁
            stamp = stampedLock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                stampedLock.unlockRead(stamp); // 释放悲观读锁
            }
        }
        // 计算距离
        double distance = Math.sqrt(currentX * currentX + currentY * currentY);
        System.out.println("乐观读操作:距离=" + distance);
        return distance;
    }
    public static void main(String[] args) throws InterruptedException {
        StampedLockDemo demo = new StampedLockDemo();
        // 模拟多线程乐观读
        for (int i = 0; i < 5; i++) {
            new Thread(demo::distanceFromOrigin, "乐观读线程-" + i).start();
        }
        // 模拟写操作
        Thread.sleep(100);
        new Thread(() -> demo.move(1.0, 2.0), "写线程").start();
        // 再次模拟乐观读
        Thread.sleep(100);
        new Thread(demo::distanceFromOrigin, "乐观读线程-5").start();
    }
}

代码解释

六、其他常见锁

锁类型核心特点作用使用场景
自旋锁获取锁失败时循环重试,不阻塞线程减少上下文切换,提升性能锁持有时间短、CPU核心多的场景
分段锁将锁拆分到多个段,仅锁当前段提升并发度ConcurrentHashMap(Java7)
偏向锁偏向第一个获取锁的线程,减少开销优化无竞争场景性能单线程访问同步资源的场景
轻量级锁无竞争时用CAS,竞争时升级为重量级平衡性能与安全低竞争的多线程场景
重量级锁依赖操作系统互斥量,阻塞线程解决高竞争场景高竞争的多线程场景

总结

  1. 基础场景选synchronized:JVM内置锁,简单易用,自动释放,适合新手或简单同步场景;
  2. 复杂场景选ReentrantLock:支持公平锁、超时、中断、尝试获取锁,灵活性更高;
  3. 读多写少选读写锁:ReentrantReadWriteLock(基础)或StampedLock(高性能),读操作并行提升并发;
  4. 低竞争选乐观锁(CAS):Atomic系列类实现,无锁操作,性能远超悲观锁;高竞争选悲观锁(synchronized/ReentrantLock)。

核心原则:根据竞争程度选择锁——竞争小用乐观锁/轻量级锁,竞争大用悲观锁/重量级锁;读多写少用读写锁,写多读少用独占锁。

到此这篇关于Java中各类锁的类型、核心作用、适用场景示例代码详解的文章就介绍到这了,更多相关java锁的类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文