1
1

More than 1 year has passed since last update.

Javaアプリをより高速化したくてReadWriteLockパターンを利用してみたところ

Last updated at Posted at 2021-12-12

◆Javaでの ReadWriteLock サンプルプログラム(改善版)  【notifyAll() の実行はCPUパワーをかなり消費し、実行速度が低下します】

  • ReadWriteLockを利用して少しでもRead処理の高速化を図るつもりが、私の利用環境&CPUパワーの少ないマシンでは、シンプルにsynchronizedでアクセス毎に該当オブジェクトをロックしてアクセスする方が明らかに高速(High performance)だったりしました。

  • readLock()&readUnlock() しか実行していないのに遅すぎるため原因を探ったところ、参考にしたプログラムでは無駄にnotifyAll() が実行されることによって速度が低下していました。そこで、notifyAll() が不要な場合は実行しないように改善してみたのが下記です。これによって無用な処理速度の低下が防止できます。キモはnotifyAll()の前に判定処理を入れたところです。

#下記は、writeLock() が優先される作り(writeLock呼び出しが続くとreadLockが取得できない)となっています。

final class ReadWriteLocks {

  private int     lockCounter = 0;
  private int     waitingReadLockCounter  = 0;
  private int     waitingWriteLockCounter = 0;
  private boolean isWriting = false;

  final synchronized int getNumber() {
    return lockCounter;
  }

  final  synchronized void readLock() {
    waitingReadLockCounter++;

    while (isWriting || waitingWriteLockCounter > 0) {
      try {
        System.out.println("readLock wait =================");
        wait();
      } catch (InterruptedException e) { }
    }

    waitingReadLockCounter--;
    lockCounter++;
  }


  final  synchronized void readUnlock() {
    lockCounter--;
    if (lockCounter == 0) {
      if (waitingWriteLockCounter != 0) {
        notifyAll(); // wait中がいる場合のみ実行
      }
    }
  }


  final  synchronized void writeLock() {
    waitingWriteLockCounter++;

    while (isWriting || lockCounter > 0) {
      try {
        System.out.println("writeLock wait =================");
        wait();
      } catch (InterruptedException e) { }
    }

    waitingWriteLockCounter--;
    isWriting = true;
  }


  final  synchronized void writeUnlock() {
    isWriting = false;

    if ((waitingReadLockCounter != 0) || (waitingWriteLockCounter != 0)) {
      notifyAll(); // wait中がいる場合のみ実行
    }
  }
}

◆Javaでの ReadWriteLock サンプルプログラム 【lock取得できなかったら寝込むバージョン】

  • notifyAll() は、呼び出されるとwait()中のスレッドが全て起こされます(順序は不定)。
    せっかく起こされて動作開始してもwhile内の条件に合致していれば再度wait()し、次の(処理の重い)notifyAll() を待つことになり、まだ少々無駄があります。

  • こちらのプログラムでは、lockが取れなければしばらく sleep(寝て)、起きたら再度lock取得を試すというとってもシンプルなものになります。readLockされている時間が短く(readLockされていない時間が長く)、write処理開始にリアルタイム性を求めない場合にはこちらが良いかと思います。

#下記は、readLock() が優先される作り(readLock呼び出しが続くとwriteLockが取得できない)となっています。

[FirstReadWriteLock.java]
-------
final class FirstReadWriteLock {

   private int lockNumber = 0;

  final synchronized int getNumber() {
    return lockNumber;
  }

  final synchronized boolean readLock() {
    if (lockNumber == -1) {
      return false;
    } else {
      lockNumber++;
      return true;
    }
  }

  final synchronized void readUnlock() {
    lockNumber--;
  }

  final synchronized boolean writeLock() {
    if (lockNumber != 0) {
      return false;
    } else {
      lockNumber = -1;
      return true;
    }
  }

  final synchronized void writeUnlock() {
    lockNumber = 0;
  }
}
-------


[HogeServer.java]
---
static FirstReadWriteLock rwLock = new FirstReadWriteLock();
---

[readLock使用]
---
try {
      while( HogeServer.rwLock.readLock() == false ) {
        System.out.println("read sleep........");
        sleep(50); // 寝込む
      }
    } catch (InterruptedException ie) {
      System.out.println("Interrupt...");
    }

//        【参照処理(read処理)を記述】

   HogeServer.rwLock.readUnlock();
---


[writeLock使用]
---
try {
      while( HogeServer.rwLock.writeLock() == false ) {
        System.out.println("write sleep........");
        Thread.sleep(50); // 寝込む
      }
    } catch (InterruptedException ie) {
      System.out.println("Interrupt...");
    }

//       【更新処理(write処理)を記述】

    HogeServer.rwLock.writeUnlock();
---

  • 初期段階で、Java標準のReentrantReadWriteLock も試しましたが、私の検証環境ではread限定の計測で遅すぎてお話にならなかったです。# 毎度 synchronizedしてread の方が圧倒的に速かった。

  ・・・OpenJDK13 にて

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1