首页 > 技术文章 > 朝花夕拾系列文章 >

线程基础(三十四)-Read-Write Lock Pattern

更新时间:2019-10-21 | 阅读量(680)

>本文作者:王一飞,叩丁狼高级讲师。原创文章,转载请注明出处。 接上篇,本篇讲解线程另外一个设计模式:Read-Write Lock Pattern. #### 概念 Read是读, 指获取/查询数据的线程, Write是写,指操作(增删改)数据的线程. Read-Write Lock 模式要求: 1>在读模式时,多个线程可以同时执行读操作,但不允许写操作 2>在写模式时,只允许一个线程执行写操作,不允许其他线程进行读写 简单讲就是常说的:读写互斥 #### 参与角色 Reader 读线程,拥有对共享资源读操作权限 Writer 写线程,拥有对共享资源写操作权限 Resource 共享资源, Reader/writer角色共享的资源, 一般具有2个方法,一个不改变资源状态的读操作, 一个可改变资源状态的写操作。 读写锁 锁对象,提供读锁, 写锁,在reader/writer角色读写时,加锁实现读写互斥 #### 演示案例 需求:5个线程读,5个线程写实现读写互斥 Read-Write Lock Pattern难点在于怎么控制读写互斥,而读写互斥可以拆分为: 1>读取与写入的冲突 2>写入与写入的冲突 3>写入与读取的冲突 思考:读写怎么互斥法 1:读模式时(线程尝试读操作) a>如果此时已经有线程在读,允许读 b>如果此时已经有线程在写,暂停当前线程 2:写模式时(线程尝试写操作) a>如果此时已经有线程在读,暂定当前线程 b>如果此时已经有线程在写,暂停当前线程 根据上面分析,很容易可以找到需求突破口 1>线程满足某个条件需要暂停等待----线程wait/notifyall操作 2>这里某个条件是当前在读/在写的线程数量-----线程计数 3>使用同一锁对象对多线程的读写互斥控制 结论: 设计:ReadWriteLock 读写锁控制类 readCount : 当前读线程数量 writeCount: 当前写线程数量 readLock(): 获取读锁,成功执行,不成功等待 unReadLock(): 操作结束后释放读锁 writeLock: 获取写锁,成功执行,不成功等待 unWriteLock: 操作结束后释放写锁 ```java //互斥锁控制类 public class ReadWriteLock { //读线程个数 private int readCount; //写线程个数 private int writeCount; public synchronized String info(){ return "读线程:" + readCount + ", 写线程:" + writeCount; } //获取读锁 public synchronized void readLock() throws InterruptedException { //此时有写线程操作,暂停 while(writeCount > 0 ){ wait(); } //没有读写线程+1 readCount++; System.out.println("读模式:" +info()); } //释放读锁 public synchronized void unReadLock(){ readCount--; //读线程减少 notifyAll(); //唤醒等待线程:读或者写 } //获取写锁 public synchronized void writeLock() throws InterruptedException { //此时有读线程操作,暂停 //此时有写线程操作,暂停 while(readCount > 0 || writeCount > 0){ wait(); } //没有读写线程+1 writeCount++; System.out.println("写模式:" +info()); } //释放写锁 public synchronized void unWriteLock(){ writeCount--; //读线程减少 notifyAll(); //唤醒等待线程:读或者写 } } ``` 操作的资源 //共享资源 ```java //共享资源 public class Resource { //读与写数据 private String data = "init"; //控制data数据的读写互斥 private ReadWriteLock lock = new ReadWriteLock(); //对data数据的读操作 public void read() throws InterruptedException { lock.readLock(); try { System.out.println(Thread.currentThread().getName()+"--read--:" + data); }finally { lock.unReadLock(); } } //对data数据的写操作 public void write(String d) throws InterruptedException { lock.writeLock(); try { data = d; System.out.println(Thread.currentThread().getName()+" -write-:" + data); }finally { lock.unWriteLock(); } } } ``` 测试类 ```java public class App { public static void main(String[] args) { final Resource data = new Resource(); //5个读 for (int i = 0; i < 5; i++) { new Thread(new Runnable() { public void run() { while (true){ try { data.read(); Thread.sleep(new Random().nextInt(200)); } catch (InterruptedException e) { e.printStackTrace(); } } } },"read_" + i).start(); } //5个写 for (int i = 0; i < 5; i++) { new Thread(new Runnable() { public void run() { while (true){ try { data.write(Thread.currentThread().getName()); Thread.sleep(new Random().nextInt(200)); } catch (InterruptedException e) { e.printStackTrace(); } } } },"write_" + i).start(); } } } ``` 执行效果: ```java 读模式:读线程:1, 写线程:0 read_0--read--:init 读模式:读线程:2, 写线程:0 read_3--read--:init 读模式:读线程:3, 写线程:0 read_2--read--:init 读模式:读线程:4, 写线程:0 read_1--read--:init 读模式:读线程:1, 写线程:0 read_4--read--:init 写模式:读线程:0, 写线程:1 write_0 -write-:write_0 写模式:读线程:0, 写线程:1 write_1 -write-:write_1 写模式:读线程:0, 写线程:1 write_2 -write-:write_2 写模式:读线程:0, 写线程:1 write_3 -write-:write_3 写模式:读线程:0, 写线程:1 write_4 -write-:write_4 写模式:读线程:0, 写线程:1 write_0 -write-:write_0 读模式:读线程:1, 写线程:0 read_3--read--:write_0 写模式:读线程:0, 写线程:1 write_4 -write-:write_4 读模式:读线程:1, 写线程:0 read_1--read--:write_4 读模式:读线程:1, 写线程:0 read_4--read--:write_4 读模式:读线程:1, 写线程:0 read_2--read--:write_4 写模式:读线程:0, 写线程:1 write_0 -write-:write_0 写模式:读线程:0, 写线程:1 write_1 -write-:write_1 读模式:读线程:1, 写线程:0 ``` 从输出效果看, 当读线程有值时,不会进行写操作, 但读操作可以同时进行,比如:读模式时,读线程:4,写线程为:0。 反之,当写线程出现1时,写线程全部为0, 另外写线程个数不会超过1 ##### 使用场景 Read-Write Lock Pattern讲究是读写互斥,读不进行安全控制, 写时需要控制。相对读写都进行安全控制的模式来说,性能上还是有一定提升, 但不绝对。Read-Write Lock Pattern更使用与读频率远高与写频率的安全操作。 jdk中其实也有读写锁操作 ```java //共享资源 public class Resource { //读与写数据 private String data = "init"; //控制data数据的读写互斥 // private ReadWriteLock lock = new ReadWriteLock(); //jdk private final ReadWriteLock lock = new ReentrantReadWriteLock(true); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); //对data数据的读操作 public void read() throws InterruptedException { //lock.readLock(); readLock.lock(); try { System.out.println(Thread.currentThread().getName()+"--read--:" + data); }finally { //lock.unReadLock(); readLock.unlock(); } } //对data数据的写操作 public void write(String d) throws InterruptedException { //lock.writeLock(); writeLock.lock(); try { data = d; System.out.println(Thread.currentThread().getName()+" -write-:" + data); }finally { // lock.unWriteLock(); writeLock.unlock(); } } } ```
叩丁狼学员采访 叩丁狼学员采访
叩丁狼头条 叩丁狼头条
叩丁狼在线课程 叩丁狼在线课程