行业资讯 2025年08月6日
0 收藏 0 点赞 457 浏览 3328 个字
摘要 :

文章目录 读写锁是什么 ReadWriteLock接口 ReentrantReadWriteLock 读写锁 基本使用 读读共享 写写互斥 读写互斥  总结 前面讲到的 synchronized 内部锁和 Reentrant……




  • 读写锁是什么
  • ReadWriteLock接口
  • ReentrantReadWriteLock 读写锁
    • 基本使用
    • 读读共享
    • 写写互斥
    • 读写互斥
  •  总结

前面讲到的 synchronized 内部锁和 ReentrantLock 都是独占锁(排他锁),同一时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低。

ReentrantLock是一种排他锁,同一时刻只允许一个线程访问,ReadWriteLock 接口的实现类 ReentrantReadWriteLock 读写锁提供了两个方法:readLock()和writeLock()用来获取读锁和写锁,也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。

读写锁是什么

读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。

总结来说,读写锁的特点是:读读共享、读写互斥、写写互斥。

ReadWriteLock接口

public interface ReadWriteLock {
    
    // 读锁
    Lock readLock();
   
    // 写锁
    Lock writeLock();
}

读写锁维护了两个锁,一个是读操作相关的锁也称为共享锁,一个是写操作相关的锁也称为排他锁。通过分离读锁和写锁,其并发性比一般排他锁有了很大提升。

ReentrantReadWriteLock 读写锁

ReentrantReadWriteLock 读写锁实现了ReadWriteLock 接口,我们先看下基本使用:

基本使用

//定义读写锁
ReadWriteLock rwLock = new ReentrantReadWriteLock();
//获得读锁
Lock readLock = rwLock.readLock();
//获得写锁
Lock writeLock = rwLock.writeLock();
//读数据
readLock.lock();
try {
    //读数据
} finally {
    readLock.unlock();
}

//写数据
try {
    writeLock.lock();
    //写数据
} finally {
    writeLock.unlock();
}

读读共享

从下面这段代码的运行状态中可以看出,创建的5个线程都同时拿到了读锁,而不用每隔3秒拿一个。说明读锁是共享的。

/**
 * ReadWriteLock读写锁允许多个线程同时读,即读读共享
 */
public class Test01 {

    static class Service {
        //定义读写锁
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void read() {
            //定义方法读取数据
            readWriteLock.readLock().lock();  //申请读锁
            try {
                System.out.println(Thread.currentThread().getName() + \"获得读锁\");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readWriteLock.readLock().unlock();  //释放读锁
            }
        }
    }

    public static void main(String[] args) {
        Service service = new Service();

        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    service.read();
                }
            }).start();
        }
    }
}

写写互斥

从下面的例子中可以看出来,写写是互斥的,每个线程必须依次获得写锁:

public class Test02 {

    static class Service {
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void write() {
            readWriteLock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + \"获得了写锁\");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
    }

    public static void main(String[] args) {
        Service service = new Service();
        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    service.write();
                }
            }).start();
        }
    }
}

读写互斥

一个线程获得读锁,写线程等待。一个线程获得写锁,其他线程等待。

public class Test03 {

    static class Service {
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private Lock readLock = readWriteLock.readLock();
        private Lock writeLock = readWriteLock.writeLock();

        public void read() {
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + \"读取数据:\" + System.currentTimeMillis());
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        }

        public void write() {
             writeLock.lock();            
             try {
                System.out.println(Thread.currentThread().getName() + \"写数据:\" + System.currentTimeMillis());
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        new Thread(new Runnable() {
            @Override
            public void run() {
                service.read();
            }
        }).start();
        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    service.write();
                }
            }).start();
            TimeUnit.MILLISECONDS.sleep(500);
        }
        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    service.read();
                }
            }).start();
        }
    }
}

 总结

多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥(只要出现写操作的过程就是互斥的)。在没有线程进行写操作时,进行读取操作的多个线程都可以获取读锁,而进行写入操作的线程只有在获取写锁后才能进行写操作。即多个线程可以同时进行读取操作,但是同一时刻只允许一个线程进行写入操作。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9699.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号