Design Pattern: Read-Write-Lock 模式如果有一个资料档有可能同时间会有许多客户端对它进行读取与写入的
Design Pattern: Read-Write-Lock 模式
如果有一个资料档有可能同时间会有许多客户端对它进行读取与写入的动作,则必须注意资料的同步问题,像是两个写入者进行写入时,后一个写入者的资料会有可能将次一个写入者的资料覆盖掉;而有时您希望读取者看到的是最新的资料,如果在读取的时候,有写入者想要对资料进行写入,则最好等待读取者读取完毕,相反的如果在写入时有客户想要读取资料,则最好等待,以确保读出来的资料是最新的资料。
读取写入的同步问题向来是难解的问题之一,有几个可行的作法,例如若有写入的动作时,则读取者以唯读模式开启;或是如果有开启资料档的动作时,无论是读取或是写入,后一个开启档案的客户都一律以唯读模式开启;还有最干脆的作法,就是将这个问题由客户决定,在开启档案时若已有其他人开启中,则提供选项让客户决定要不要以唯读模式开启,通常这个作法是提供给档案的拥有者使用。
Read-Write-Lock 模式提供给被读取或写入的资料“一把锁”,在读取或写入时都必须先取得这把锁,读取的客户可以同时共同这把锁,而写入的客户也可以共用这把锁,但读取不可与写入共用一把锁,如果尝试取得锁时发现锁已经被另一方取得,则等待直到锁被释放并重新取得它。
下图读取者读取资料时的Sequence Diagram示例:
现在假设读取者已经取得锁,而写入者试图进行写入,它也试图先取得锁定,但发现锁已经被读取的一方拥有,于是先进入等待,直到读取的一方解除锁定为止:
一个简单的Java程式例子如下所示:
?public void readData() {
??? lock.readLock();
??? doRead();
??? lock.readUnLock();
?}
?public void writeData() {
??? lock.writeLock();
??? doWrite();
??? lock.writeUnLock();
?}
?
而最主要的关键还是在于锁的实现,在Java中可以用wait()、notify()来实现,实现的片段如下:
?private boolean writerFirst = true; // 写入优先
?
?public synchronized void readLock() {
??? try {
??????? while(writingWriters > 0 ||
?????????????????? (writerFirst && waitingWriters > 0)) {
??????????? wait();
??????? }
??? }
??? catch(InterruptedException) {
??? }
??? readingReaders++;
?}
?
?public synchronized void readUnLock() {
??? readingReaders--;
??? writerFirst = true;
??? notifyAll();
?}
?
?public synchronized void writeLock() {
??? waitingWriters++
??? try {
??????? while(readingReaders > 0 || writingWriters > 0) {
??????????? wait();
??????? }
??? }
??? catch(InterruptedException) {
??? }
??? finally {
??????? waitingWriters--;
??? }
??? writingWriters++;
?}
?
?public synchronized void writeUnLock() {
??? writingWriters--;
??? writerFirst = false;
??? notifyAll();
?}
?
其中writerFirst是写入优先的旗标,它确保只要有写入的执行绪在等待时,在解除锁定的时候,可以优先由写入执行绪取得锁定,以确保读取者读取到的资料可以是最新的,但缺点就是写入的动作很频繁时,读取者必须等待的机会将增多,相反的若设定为读取优先,则读取时的回应性会增高,但资料更新的速率将会下降,实际使用时要偏好哪一方,必须视应用的场合而定。