轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

    • 并发

      • Java并发-线程基础与synchronized关键字
      • Java并发-重入锁ReetrantLock的使用
      • Java并发-信号量Semaphore
      • Java并发-读写锁ReadWriteLock
        • 改进版的读写锁:StampedLock
      • Java并发-倒计时器CountDownLatch
      • Java并发-栅栏CyclicBarrier
      • Java并发-LockSupport线程阻塞工具类
      • Java并发-线程池ThreadPoolExecutor
      • Java并发-阻塞队列BlockingQueue
      • Java并发-以空间换时间之ThreadLocal
      • Java并发-无锁策略CAS与atomic包
      • Java并发-JDK并发容器
      • Java并发-异步调用结果之Future和CompletableFuture
      • Java并发-Fork Join框架
      • Java并发-调试与诊断
    • 经验

    • JVM

    • 企业应用

  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 并发
轩辕李
2018-06-08
目录

Java并发-读写锁ReadWriteLock

今天讲另一个并发工具,叫读写锁。读写锁是一种分离锁,是锁应用中的一种优化手段。
考虑读多写少的情况,这时如果我们用synchronized或ReentrantLock直接修饰读/写方法未尝不可,如:

public static class Rw {
    private int val;

    public synchronized int read() throws InterruptedException {
        Thread.sleep(1000);
        return val;
    }

    public synchronized void write(int value) throws InterruptedException {
        Thread.sleep(1000);
        this.val = value;
    }
}

简单有效。
如果场景是读的情况比较多,而你想做点优化的话,可能要用到读写锁ReadWriteLock了。

先看代码:

public static class Rw {
    private int val;

    public int read(Lock lock) throws InterruptedException {
        try {
            lock.lock();
            Thread.sleep(1000);
            return val;
        } finally {
            lock.unlock();
        }
    }

    public void write(Lock lock, int value) throws InterruptedException {
        try {
            lock.lock();
            Thread.sleep(1000);
            this.val = value;
        } finally {
            lock.unlock();
        }
    }
}

调用:

private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public static void main(String[] args) {
    final Rw rw = new Rw();
    
    for (int i = 18; i < 20; i++) {
        new Thread(() -> {
            try {
                rw.write(readWriteLock.writeLock(),1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    for (int i = 0; i < 18; i++) {
        new Thread(() -> {
            try {
                int read = rw.read(readWriteLock.readLock());
                System.out.println(read);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

ReentrantReadWriteLock是ReadWriteLock的常用子类。
代码逻辑比较简单,读的时候上读锁,写的时候上写锁。只有写的时候会阻塞等待,这样在读情况较多的场景下,执行效率大大提升了。
这里面只需要注意一个问题:所有和写相关的都会阻塞,比如读-写同时进行,也会阻塞。

# 改进版的读写锁:StampedLock

读写锁分离了读和写的功能,但读和写之间依然是冲突的。
在Java 8中提供了StampedLock,他提供了一种乐观的锁策略,类似于无锁,使得乐观锁不会阻塞写线程。

public class Point {

    private double x, y;
    private final StampedLock sl = new StampedLock();

    void move(double deltaX, double deltaY) { //这是一个排它锁
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            s1.unlockWrite(stamp);
        }
    }

    double distanceFrom0rigin() { //只读方法
        long stamp = s1.tryOptimisticRead();
        double currentX = x, currentY = y;
        if (!sl.validate(stamp)) {
            stamp = s1.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl, unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

从代码中可以看到,写的时候依然是个排它锁。
重点在于读的时候,先通过tryOptimisticRead()获得一个stamp邮戳,这是一个锁凭证。然后使用validate(stamp)验证凭证是否过期--也就是期间有没有发生过写的情况,如果发生过,则有两种处理方式:

  • 像CAS操作中一样,在一个死循环中一直读取乐观锁,直到成功
  • 乐观锁升级为悲观锁,也就是实例代码中采用的方式

StampedLock实现了读写之间的不冲突,无疑是一种更效率的方式。

编辑 (opens new window)
#ReadWriteLock
上次更新: 2023/06/14
Java并发-信号量Semaphore
Java并发-倒计时器CountDownLatch

← Java并发-信号量Semaphore Java并发-倒计时器CountDownLatch→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式