1 Read-Write Lock Pattern

Read-Write Lock Pattern是一种将对于共享资源的访问与修改操作分离,称为读写分离。即访问是reader,修改是write,用单独的线程来处理。可以允许多个reader,但是不允许同时多个写入或者在读的过程中有写入。

由于对于实例状态的读取,并不会破坏状态的完整性且状态也不会修改,可以允许多个线程同时访问操作。但是若在写入的过程中,会更改实例的状态,此时就需要对于写入做保护,防止其他线程来进行读操作和写操作。

在多个线程共享一个实例的时候,会有参考实例状态的线程即仅仅读取实例状态的Reader参与者,并且也会有改变实例的状态的线程即Writer参与者,此时就需要运用这种模式。

这种模式在读取不会冲突下,允许多个Reader参与者同时reader,提高了性能。不用单纯的在每次读的时候利用Synchronized来使得只能允许一个线程读操作,而是利用了外部定义的逻辑锁来设置允许多个Reader同时操作。在读取的操作比写入的操作多的时候,也可以使用这个模式。

在这个模式中,主要有四个参与者,分别为:

1 Reader参与者。Reader参与者会对SharedResource参与者进行read。

2 Writer参与者。Writer参与者会对SharedResource参与者进行write。

3 SharedResource参与者。这个是Reader参与者与Writer参与者共享的资源。里面包含了提供不会改变内部状态的操作read,与会改变内部状态的操作write。注意对于这个参与者,不需要在进行read与write的方法加上synchronized。

在对于SharedResource参与者进行read和write操作时候,利用Before/After Pattern实现。

前置处理(获取锁定)

try{

实际操作

}finally{

后续处理(释放锁)}

利用finally可以保证无论发生什么情况,都会释放锁,也不能把前置处理放置在try中,如果放置在里面,则finally就一定会执行,当前置处理发生异常,就不应该进而执行finally。

4 ReadWriteLock参与者。提供了对于SharedResource参与者进行read和write时需要的锁,就是用户自定义的锁机制。包括readLock和readUnLock锁,writeLock和writeUnLock,注意必须要对这些操作进行同步处理,放置多个线程同时调用这些方法,在每个方法中必须加上synchronized。

实例:

有多个读者和多个写者共同操作一本书,只允许同时多个读者读取书,只允许同时一个写者写这本书,不允许同时读者与写者同时操作这本书籍。

SharedResource参与者。

在每次读写操作之间和之后都要获取锁与释放锁,不需要synchronized

package whut.readwritelock;
public class Data {
private final char[] buffer;
private final ReadWriteLock lock=new ReadWriteLock(); public Data(int size)
{
this.buffer=new char[size];
for(int i=0;i<buffer.length;i++)
buffer[i]='*';
} public char[] read()throws InterruptedException
{
lock.readLock();
try{
return doRead();
}finally{
lock.readUnlock();
}
} private char[] doRead()
{
char[] newbuf=new char[buffer.length];
for(int i=0;i<buffer.length;i++)
newbuf[i]=buffer[i];
slowly();
return newbuf;
} public void write(char c)throws InterruptedException
{
lock.writeLock();
try{
doWrite(c);
}finally{
lock.writeUnlock();
}
} private void doWrite(char c)
{
for(int i=0;i<buffer.length;i++)
{
buffer[i]=c;
slowly();
//这里的sleep并不会切换到别的线程
//这里就是体现了使用while的好处
//当该线程sleep时候,其余等待读取的还在wait中,而要写入的线程会判断它的状态,还没有释放锁
}
} private void slowly()
{
try{
Thread.sleep(50);
}catch(InterruptedException e)
{ }
} }

读者线程Reader

package whut.readwritelock;
public class ReaderThread extends Thread{
private final Data data;
public ReaderThread(Data data)
{
this.data=data;
} public void run()
{
try{
while(true)
{
char[] readbuf=data.read();
System.out.println(Thread.currentThread().getName()
+" reads "+String.valueOf(readbuf));
}
}catch(InterruptedException e)
{
}
}
}

写者线程Writer

package whut.readwritelock;
import java.util.Random;
public class WriterThread extends Thread{ private static final Random random=new Random();
private final Data data;
private final String filler;
private int index=0; public WriterThread(Data data,String filler)
{
this.data=data;
this.filler=filler;
} public void run()
{
try{
while(true)
{
char c=nextChar();
data.write(c);
Thread.sleep(random.nextInt(1000));
}
}catch(InterruptedException e)
{ }
} private char nextChar()
{
char c=filler.charAt(index);
index++;
if(index>=filler.length())
index=0;
return c;
}
}

WriteReadLock类

主要就是利用自定义锁机制来控制读写操作。设置读写时的警戒条件,利用Guarded Suspension Pattern来处理。

设置有四个字段:

readingReaders表示实例当前被读取的线程数目。是readLock之后与readUnlock之间的数。作为写入操作的警戒条件之一。

writingReaders表示实例当被写操作的线程数目,只能是0或者1,它是作为写入操作和读取操作的警戒条件。

waitingReaders表示多少个处于等待写入的线程数目。当ReaderThread比WriterThread多的时候,由于在要进行写入操作过程中,如果有读取操作,则会一直等待,但是读取操作没有设置为互斥,则他们会一个个的执行读操作,致使写入操作无法进行。设置这个字段状态目的就是为了使得ReaderThread在警戒条件判断中当waitingReaders大于0的时候,该ReaderThread能wait,能让出控制权给WriterThread。

preferWriter表示true为写优先,表示false为读优先。虽然读取能让出控制权给写入操作,可是当写入操作拥有了控制权后,则可能使得读取操作无法执行。此时就需要这个preferWriter字段,这个就是目的使得ReaderThread和WriterThread能够轮流执行的。

当读操作完后,设置为true以使得写入操作有可能获得执行,不过写入操作必须在满足preferWriter为true以及有等待写入的操作下才能真正的写入。当写入操作完毕后,设置该字段为false使得读操作能够执行。

总结:

读取操作等待的警戒条件是:当前写入线程大于0,或者preferWriter为true且有等待写入的线程

写入操作等待的警戒条件是:当前写入线程大于0,或者当前读取线程大于0

package whut.readwritelock;
//关键部分
public class ReadWriteLock {
private int readingReaders=0;//实际正在读取的线程数目
private int waitingWriters=0;//正在等待写入的线程数目
private int writingWriters=0;//实际正在写入的线程数目
private boolean preferWriter=true;//写入优先的话,值为true //读取的时候获取锁
public synchronized void readLock()throws InterruptedException
{
//当有写入的时候,或者写入为优先级并且有等待的写入线程
while(writingWriters>0||(preferWriter&&waitingWriters>0))
{
wait();
}
readingReaders++;
} //读完毕后释放锁
public synchronized void readUnlock()throws InterruptedException
{
readingReaders--;
preferWriter=true;
notifyAll();
} //写入的时候获取锁
public synchronized void writeLock()throws InterruptedException
{
waitingWriters++;//正在等待的写入的线程数目
try{
//有写入或者读入的时候
while(readingReaders>0||writingWriters>0)
{
wait();
}
}finally{
waitingWriters--;//被唤醒了,则就是进而真正写入
}
writingWriters++;
} //写入毕后释放锁
public synchronized void writeUnlock()throws InterruptedException
{
writingWriters--;
preferWriter=false;//写入后马上更换优先级,让读者继续
notifyAll();
}
}

测试类:

package whut.readwritelock;
public class ReadWriteMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Data data=new Data(10);
//读取线程
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start(); //写入线程
new WriterThread(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").start();
new WriterThread(data,"abcdefghijklmnopqrstuvwxyz").start();
}
}

java多线程设计模式(3)读写锁模式的更多相关文章

  1. Java多线程13:读写锁和两种同步方式的对比

    读写锁ReentrantReadWriteLock概述 大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务 ...

  2. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现.该线程模式 ...

  3. Java多线程编程之读写锁【ReentrantReadWriteLock】

    有时候我们需要有这样的需求:        对于同一个文件进行读和写操作,普通的锁是互斥的,这样读的时候会加锁,只能单线程的读,我们希望多线程的进行读操作,并且读的时候不能进行写操作,写的时候不能进行 ...

  4. java多线程:ReentrantReadWriteLock读写锁使用

    Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...

  5. java多线程设计模式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt220 java多线程设计模式 java语言已经内置了多线程支持,所有实现Ru ...

  6. [温故]图解java多线程设计模式(一)

    去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~  1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...

  7. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

  8. java并发锁ReentrantReadWriteLock读写锁源码分析

    1.ReentrantReadWriterLock 基础 所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读 ...

  9. java 可重入读写锁 ReentrantReadWriteLock 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...

随机推荐

  1. centos6 install cobbler

    cobbler 安装   一:定义yum源 wget -c -O CentOS-Base.repo http://mirrors.163.com/.help/CentOS6-Base-163.repo ...

  2. 简述Shiro验证过程

    如果让我们自己实现用户登录验证我们都需要哪些步骤? 很简单,根据用户提供的账号从数据库中查询该账户的密码以及一些其他信息,然后拿这个密码与用户输入的密码相比较,因为保存在数据库中的密码一般是经过加密的 ...

  3. Leetcode 661.图片平滑器

    图片平滑器 包含整数的二维矩阵 M 表示一个图片的灰度.你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个 ...

  4. Client does not support authentication protocol requested by server

    关于由于版本号码不同而引起的 Client does not support authentication protocol requested by server 问题 搜索类似的问题,得到的答案类 ...

  5. 【linxu】部署docker容器虚拟化平台

    实验所涉内容 Docker 概述 部署 docker 容器虚拟化平台 docker 平台基本使用方法 创建docker镜像和发布镜像方法 Container 容器端口映射 一 Docker 概述 Do ...

  6. 【bzoj2238】Mst 最小生成树+树链剖分+线段树

    题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...

  7. 【bzoj1565】[NOI2009]植物大战僵尸 拓扑排序+最大权闭合图

    原文地址:http://www.cnblogs.com/GXZlegend/p/6808268.html 题目描述 输入 输出 仅包含一个整数,表示可以获得的最大能源收入.注意,你也可以选择不进行任何 ...

  8. [CF999E]Reachability from the Capital

    题目大意:有一个$n$个点$m$条边的有向图,起点$S$,要求你添加最少的边使得$S$可以到达所有点 题解:缩点,答案就是没有入边的强连通分量个数,注意,如果起点$S$所在的强连通块没有入边则不计入答 ...

  9. Python空间数据处理环境搭

    Python空间数据处理环境搭 Conda的下载和安装 什么是Conda? 官方定义:Package, dependency and environment management for any la ...

  10. ZOJ Bizarre Routine 2013杭州网赛B题

    题目意思: 给定n, expect, a, b 要求你构造一组array[],存放一个1..n的排列,使的下面的程序能输出YES 题目所示代码: bool less_than(x, y) { T++; ...