【デザインパターン】【非同期】Read-Write Lock パターン

仕様

 * 複数個のスレッドで同時に読みのは受け付ける
 * 読んでいる間は書き込めない
  => 逆に言うと、書いている間は、他のスレッドは読むことはできない

利点

 * 読む処理同士は、排他されないのでパフォーマンスが上がる

サンプル

 * java.util.concurrent でもっと簡単に書けるらしいが、勉強なので真面目に書く
http://blogs.yahoo.co.jp/dk521123/32538961.html

Main.java

public class Main {
   public static void main(String[] args) {
       DataStocker stocker = new DataStocker(10);
       new ReaderThread(stocker).start();
       new ReaderThread(stocker).start();
       new ReaderThread(stocker).start();
       new ReaderThread(stocker).start();
       new ReaderThread(stocker).start();
       new WriterThread(stocker, "ABCDEFGHIJ").start;
       new WriterThread(stocker, "0123456789").start;
   }
}

DataStocker.java

SharedResource
public class DataStocker {
   private final char[] targetData; // ここが、読み書き対象のデータ本体
   private final ReadWriteLock lock = new ReadWriteLock();

   public DataStocker(int size) {
       this.targetData = new char[size];
       for (int i = 0; i < this.targetData.length; i++) {
           this.targetData[i] = '*';
       }
   }

   public List<Person> readData() throws InterruptedException {
       this.lock.readLock();
       try {
           return read();
       } finally {
           this.lock.readUnlock();
       }
   }
   public void writeData(char value) throws InterruptedException {
       this.lock.writeLock();
       try {
           return write(value);
       } finally {
           this.lock.writeUnlock();
       }
   }

   private List<Person> read() throws InterruptedException {
       char[] newData = new char[this.targetData.lenght];
       for (int i = 0; i < this.targetData.length; i++) {
           newData[i] = this.targetData[i];
       }
       this.slow();
       return newData;
   }
   private void write(char value) throws InterruptedException {
       for (int i = 0; i < this.targetData.length; i++) {
           targetData[i] = value;
           this.slow();
       }
   }
   // For Test
   private void slow() {
       try {
           Thread.sleep(100);
       } catch (Exception ex) {
       }
   }
}

ReaderThread.java

Reader
public class ReaderThread extends Thread {
    private final DataStocker stocker;
    public WriterThread(DataStocker stocker) {
         this.stocker = stocker;
    }
    public void run() {
         try {
             while(true) {
                  char[] readValues = this.stocker.readData();
                  System.out.println("Result : " + Thread.currentThread().getName() +
                       " Read Values : " + String.valueOf(readValues));
             }
         } catch (Exception ex) {
         }
    }
}

WriterThread.java

Writer
public class WriterThread extends Thread {
    private static final Random random = new Random();
    private final DataStocker stocker;
    private final String stockData;
    private int index = 0;
    public WriterThread(DataStocker stocker, String stockData) {
         this.stocker = stocker;
         this.stockData = stockData;
    }
    public void run() {
         try {
             char value = this.nextValue();
             this.writeData(value);
             Thread.sleep(random.nextInt(3000));
         } catch (Exception ex) {
         }
    }
    private char nextValue() {
         char value = this.filler.charAt(this.index);
         this.index++;
         if (this.index >= this.stockData.length()) {
               this.index = 0;
         }
         return value;
    }
}

ReadWriteLock.java

ReadWriteLock
ReentrantLock(再入可能ロック)と同じ?
public final class ReadWriteLock {
     private int readingThreadNumber = 0;
     private int waitingWriterThreadNumber = 0;
     private int writingThreadNumber = 0;
     private boolean isReadyForWriter = true;

     public synchronized void readLock() throws InterruptedException {
           while (this.writingThreadNumber > 0 ||
                 (this.isReadyForWriter && this.waitingWriterThreadNumber > 0)) {
                 wait();
           }
           this.readingThreadNumber++;
     }
     public synchronized void readUnlock() {
           this.readingThreadNumber--;
           this.isReadyForWriter = true;
           notifyAll();
     }
     public synchronized void writeLock() throws InterruptedException {
           this.waitingWriterThreadNumber++;
           try {
               while (this.readingThreadNumber > 0 || writingThreadNumber > 0) {
                     wait();
               }
           } fianlly {
               this.waitingWriterThreadNumber--;
           }
           this.writingThreadNumber++;
     }
     public synchronized void writeUnlock() {
           this.writingThreadNumber--;
           this.isReadyForWriter = false;
           notifyAll();
     }
}

登場人物

Reader

 * データを読むクラス

Writer

 * データを書き込むクラス

SharedResource

 * 共有されている資源クラス

ReadWriteLock

 * 読み書き用クラス

関連するデザインパターン

Immutable パターン

 * 登場人物「Reader」で排他制御をなくするために使用

Single Threaded Execution パターン

 * 登場人物「Writer」のスレッドを1個に限定するために使用

Guarded Suspension パターン

 * 登場人物「ReadWriteLock」を作る時に使用
http://blogs.yahoo.co.jp/dk521123/32928012.html

Before/After パターン

 * ロックの解放忘れを防ぐために使用

Strategized Locking パターン

 * 排他制御のパフォーマンスにおいて、更に柔軟な排他制御を実現しているらしい。


関連記事

デザインパターン / マルチスレッド の分類 ~目次~

http://blogs.yahoo.co.jp/dk521123/34195603.html