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. Kickstart配置文件解析

    参考:https://www.douban.com/note/270359374/?type=likehttp://blog.51cto.com/molinux/548247http://debugo ...

  2. user-select & css

    user-select & css https://developer.mozilla.org/en-US/docs/Web/CSS/user-select https://css-trick ...

  3. 真·APIO2018滚粗记

    有人说只有大佬才会说滚粗啊爆零啊…… 然而我真滚粗真爆零啊…… D1: 听课,然后夏眠. 咦折纸……哦好吧太神仙了. 咦AI……好妙啊好妙啊. 咦二分……哇还有wqs二分这种神奇操作,学学学. (我是 ...

  4. 洛谷 P4171 [JSOI2010]满汉全席 解题报告

    P4171 [JSOI2010]满汉全席 题目描述 满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中.由于菜色众多而繁杂,只有极少數博学多闻技艺高 ...

  5. [转]busybox登陆后没要求输入密码的解决办法

    转自:http://blog.chinaunix.net/uid-8058395-id-65785.html 1.制作好ramdisk之后 通过串口进入系统 却发现系统直接登录进去了 并没有要求用ro ...

  6. Linux环境准备20160921

    这篇文章,是我准备linux的java环境时候,碰到的各种问题,采用的是centos 6.5版本. 1.卸载open jdk  先查看 rpm -qa | grep java  # java-1.4. ...

  7. ubuntu xampp

      1.下载:首先通过wget下载 xampp linux 1.7.3a版本,地址为http://sourceforge.net/projects/xampp/files/XAMPP%20Linux/ ...

  8. HDU 1841 Find the Shortest Common Superstring----KMP

    题意:给两个字符串,问包含这两个字符串的最小的字符串的长度 kmp返回匹配串长度 #include "iostream" #include<cstdio> #inclu ...

  9. 汕头市队赛SRM15

    T1——czl SRM 15 众所周知,czl家养了一只可♂爱的***(已屏蔽),那只东西很贪吃,所以czl家很多零食仓库,然而这些仓库里有很多老鼠. 为了心爱的***,czl决定点燃纯艾条,用烟熏老 ...

  10. Windows基础-使用XAudio2播放音频(本质是WASAPI)

    对于常见的音频播放,使用XAudio2足够了. 时间是把杀猪刀,滑稽的是我成了猪 早在Windows Vista中,M$推出了新的音频架构UAA,其中的CoreAudio接替了DSound.WaveX ...