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(); } 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(); }