并发编程实战13-同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock

  1. synchronized同步锁
    • synchronized属于悲观锁,直接对区域或者对象加锁,性能稳定,可以使用大部分场景。
  2. ReentrantLock可重入锁(Lock接口)
    • 相对于synchronized更加灵活,可以控制加锁和放锁的位置
    • 可以使用Condition来操作线程,进行线程之间的通信
    • 核心类AbstractQueuedSynchronizer,通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。
  3. ReentrantReadWriteLock可重入读写锁(ReadWriteLock接口)
    • 相对于ReentrantLock,对于大量的读操作,读和读之间不会加锁,只有存在写时才会加锁,但是这个锁是悲观锁
    • ReentrantReadWriteLock实现了读写锁的功能
    • ReentrantReadWriteLock是ReadWriteLock接口的实现类。ReadWriteLock接口的核心方法是readLock(),writeLock()。实现了并发读、互斥写。但读锁会阻塞写锁,是悲观锁的策略。
  4. StampedLock戳锁
    • ReentrantReadWriteLock虽然解决了大量读取的效率问题,但是,由于实现的是悲观锁,当读取很多时,读取和读取之间又没有锁,写操作将无法竞争到锁,就会导致写线程饥饿。所以就需要对读取进行乐观锁处理。
    • StampedLock加入了乐观读锁,不会排斥写入
    • 当并发量大且读远大于写的情况下最快的的是StampedLock锁

StampedLock控制锁有三种模式(排它写,悲观读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问。

StampedLock主要使用的是乐观读取,通过一个stamp变量来检测是否有读写冲突,每次乐观读取时会加入stamp校验。当存在写入操作时,写操作会改变stamp的状态,就会导致乐观读取失败,乐观读锁就会升级为悲观读锁

package com.sound.daytd5;

import java.util.concurrent.locks.StampedLock;

/**
 * @author: ZouTai
 * @date: 2018/4/17
 * @description:
 */
public class StampedLockDemo {
    private int balance;
    private final StampedLock stampedLock = new StampedLock();


    /**
     * 1、悲观写
     * writeLock():典型的cas操作,如果STATE等于s,设置写锁位为1(s+WBIT)。
     * acquireWrite跟acquireRead逻辑类似,先自旋尝试、加入等待队列、直至最终Unsafe.park()挂起线程。
      */
    public void write(int i) {
        long stamp = stampedLock.writeLock();
        balance += i;
        stampedLock.unlockWrite(stamp);
    }

    /**
     * 2、悲观读
     * 乐观锁失败后锁升级为readLock():尝试state+1,用于统计读线程的数量,
     * 如果失败,进入acquireRead()进行自旋,通过CAS获取锁。
     * 如果自旋失败,入CLH队列,然后再自旋,
     * 如果成功获得读锁,激活cowait队列中的读线程Unsafe.unpark(),
     * 最终依然失败,Unsafe().park()挂起当前线程。
     */
    public void read() {
        long stamp = stampedLock.readLock();
        int value = balance;
        stampedLock.unlockRead(stamp);
    }

    /**重点:!!!
     * 3、乐观读:当读取远远大于写入时,使用乐观锁
     * tryOptimisticRead():如果当前没有写锁占用,返回state(后7位清0,即清0读线程数),如果有写锁,返回0,即失败。
     */
    public void optimisticRead() {
        long stamp = stampedLock.tryOptimisticRead();
        int value = balance;
        // 校验这个戳是否有效validate():比较当前stamp和发生乐观锁得到的stamp比较,不一致则失败。
        if(!stampedLock.validate(stamp)) {
            long readStamp = stampedLock.readLock();
            value = balance;
            stamp = readStamp;
        }
        stampedLock.unlockRead(stamp);
    }

    /**重点:!
     * 4、判断条件之后,再写
     * 存在读取和写入两个操作
     */
    public void conditionReadWrite(int state){
        // 首先读取
        long stamp = stampedLock.readLock();
        while (balance > 100){
            long writeStamp = stampedLock.tryConvertToWriteLock(stamp);
            // 步骤:a
            if(writeStamp!=0) {
                balance += state;
                stamp = writeStamp;
                break;
            } else {
                // 转换失败
                stampedLock.unlockRead(stamp);
                //显式直接进行写锁 然后再通过循环再试,回到 步骤:a
                stamp = stampedLock.writeLock();
            }
        }
    }

}

同步中四种锁的性能比较
同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock
同步锁参考英文文档

<think>好的,我现在需要比较ReentrantLockReentrantReadWriteLockStampedLocksynchronized以及Condition这几个Java并发工具。首先,我得回忆每个的基本特性和使用场景,然后找出它们的异同点。 首先,synchronized是Java内置的关键字,用于方法或代码块同步,自动获取和释放,使用起来简单。但它的缺点是功能相对单一,比如无法实现公平,不能中断等待的线程,也没有尝试获取的机制。 接下来是ReentrantLock,属于java.util.concurrent包,提供了比synchronized更灵活的机制。它是可重入的,支持公平和非公平,可以中断的获取,还能设置超时时间。但需要手动释放,容易忘记导致死。 然后是ReentrantReadWriteLock,读写,分离了读和写的操作,适用于读多写少的场景。读是共享的,写是独占的,提高了并发性能。不过可能存在写线程饥饿的问题,即读长时间占用导致写无法获取。 StampedLock是Java 8引入的,提供了乐观读、悲观读和写。特别是乐观读,不阻塞写操作,适用于读远多于写的情况。但它的API更复杂,且不支持重入,使用不当容易出错。 Condition接口通常与ReentrantLock配合使用,提供了更细粒度的线程通信机制,可以创建多个条件队列,比如实现生产者消费者模型中的不同条件等待。 现在需要比较它们的特点,比如可重入性、类型、使用复杂度、性能等。还要注意使用场景,比如高并发读时StampedLock可能更高效,而synchronized在简单场景下更方便。 可能还需要考虑的公平性、中断支持、是否支持尝试获取等特性。比如ReentrantLock支持公平和非公平,而synchronized只有非公平。StampedLock则没有公平策略。 另外,要提到Condition与synchronized中的wait/notify的区别,比如Condition可以有多个等待队列,更灵活。 总结时应该指出各自的适用场景,帮助用户根据具体需求选择合适的机制。</think>以下是Java中常见机制的对比分析,按照功能特性和使用场景进行分类说明: 1. **synchronized**(同步- 类型:隐式(JVM管理) - 特性: - 自动获取/释放(基于代码块或方法) - 非公平 - 可重入性支持 - 线程阻塞不可中断 - 示例: ```java public synchronized void method() { /* 同步方法体 */ } ``` 2. **ReentrantLock**(可重入- 类型:显式(需手动释放) - 特性: - 支持公平/非公平策略(构造参数控制) - 可重入性 - 获取可中断(lockInterruptibly()) - 超时尝试获取(tryLock()) - 支持Condition条件队列 - 示例: ```java Lock lock = new ReentrantLock(); lock.lock(); try { /* 临界区 */ } finally { lock.unlock(); } ``` 3. **ReentrantReadWriteLock**(读写- 类型:分离式 - 特性: -共享(允许多线程并发读) -独占(互斥访问) - 支持降级(写→读- 存在写线程饥饿风险 - 适用场景:读多写少(如缓存系统) - 示例: ```java ReadWriteLock rwLock = new ReentrantReadWriteLock(); Lock readLock = rwLock.readLock(); Lock writeLock = rwLock.writeLock(); ``` 4. **StampedLock**(邮戳- 类型:混合(Java 8+) - 特性: - 乐观读模式(不阻塞写操作) - 支持升级/降级 - 不可重入 - 没有公平策略 - 适用场景:超高并发读(需配合版本校验) - 示例: ```java StampedLock stampedLock = new StampedLock(); long stamp = stampedLock.tryOptimisticRead(); if (!stampedLock.validate(stamp)) { stamp = stampedLock.readLock(); try { /* 读取数据 */ } finally { stampedLock.unlockRead(stamp); } } ``` 5. **Condition** - 类型:线程通信机制 - 特性: - 必须配合显式(如ReentrantLock)使用 - 支持多条件队列(对比synchronized的单队列) - 提供精准唤醒(signal()替代notify()) - 典型应用:生产者-消费者模型 - 示例: ```java Lock lock = new ReentrantLock(); Condition notEmpty = lock.newCondition(); lock.lock(); try { while (isEmpty()) notEmpty.await(); // 处理逻辑 } finally { lock.unlock(); } ``` **对比总结表格**: | 特性 | synchronized | ReentrantLock | ReadWriteLock | StampedLock | Condition | |--------------------|--------------|---------------|---------------|-------------|----------------| | 类型 | 隐式 | 显式 | 显式 | 显式 | 通信机制 | | 公平策略 | ❌ | ✔️ | ✔️ | ❌ | - | | 可重入 | ✔️ | ✔️ | ✔️ | ❌ | - | | 读写分离 | ❌ | ❌ | ✔️ | ✔️ | - | | 乐观读 | ❌ | ❌ | ❌ | ✔️ | - | | 中断 | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | | 超时机制 | ❌ | ✔️ | ✔️ | ✔️ | ✔️(通过) | | 多条件队列 | ❌ | ❌ | ❌ | ❌ | ✔️ | **选型建议**: - 简单同步:优先使用`synchronized`(自动管理,代码简洁) - 需要高级功能(公平性/中断等):选择`ReentrantLock` - 读多写少场景:优先考虑`StampedLock`(超高并发读)或`ReadWriteLock` - 复杂线程通信:使用`Condition`配合显式 - 注意:`StampedLock`需要严格校验版本,API使用不当易引发问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值