ReentrantLockのロック待ちスレッドの確認

Spring BootのAPIでReentrantLockを使用し、ロック待ちのスレッドを全て出力する方法を説明します。以下の手順で実装を進めます。

1. ReentrantLockの拡張
ReentrantLockのgetQueuedThreads()メソッドはprotectedであるため、これを利用するにはReentrantLockを拡張してカスタムクラスを作成します。

2. Spring BootのAPIに組み込む
Spring Bootのコントローラーで、ロック待ちのスレッドを出力するエンドポイントを作成します。

以下に、具体的なコード例を示します。

コード例
### 1. カスタムReentrantLockクラスの作成
ReentrantLockを拡張して、待機しているスレッドを取得するメソッドを公開します。
```java
import java.util.concurrent.locks.ReentrantLock;
import java.util.Collection;

public class CustomReentrantLock extends ReentrantLock {
    // 待機しているスレッドのコレクションを取得するメソッドを公開
    public Collection<Thread> getQueuedThreadsPublic() {
        return super.getQueuedThreads();
    }
}
```
### 2. Spring Bootのコントローラーの作成
Spring Bootのコントローラーで、ロック待ちのスレッドを出力するエンドポイントを作成します。
```java
package com.example.HelloSpringBoot.controller;

import com.example.HelloSpringBoot.controller.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CacheController {
  private int counter = 0;
  @Autowired
  private CacheService<String, String> cacheService;

  @GetMapping("/cache/lock/{key}")
  public String getFromCache(@PathVariable String key) {
    System.out.println("There are requests for the lock:"+key+String.valueOf(counter++));
    return cacheService.getOrCompute(key);
  }

  @GetMapping("/cache/wait/{key}")
  public String getWaitingThreads(@PathVariable String key) {
    // ロック待ちのスレッド
    return cacheService.getWaitingThreads(key);
  }
}
```
### 3. Spring BootのServiceの作成
Spring BootのServiceで、ロック待ちのスレッドを出力するコードを作成します。
```java
package com.example.HelloSpringBoot.controller;

import com.example.HelloSpringBoot.CustomReentrantLock;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

@Service
public class CacheService<K, V> {
  private final ConcurrentHashMap<String, CustomReentrantLock> cache = new ConcurrentHashMap<>();

  private int counter = 0;

  public String getOrCompute(String key) {
    CustomReentrantLock lock = cache.computeIfAbsent(key, k -> new CustomReentrantLock());
    cache.forEach((k , value) -> System.out.println("k:"+k+" value:"+value));
    System.out.println("lock.getQueueLength():"+lock.toString() + "::" + lock.getQueueLength());

    System.out.println("------------------------------------------");
    lock.lock();
    try {
      Thread.sleep(40000);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }

    lock.unlock();
    return key;
  }

  public String getWaitingThreads(String key) {
    // ロック待ちのスレッドが存在するか確認
    CustomReentrantLock lock = cache.computeIfAbsent(key, k -> new CustomReentrantLock());
    System.out.println("getWaitingThreads:"+lock.toString() + "::" + lock.getQueueLength());
    if (lock.hasQueuedThreads()) {
      StringBuilder response = new StringBuilder("Threads waiting for the lock:<br>");

      // ロック待ちのスレッドのコレクションを取得
      Collection<Thread> waitingThreads = lock.getQueuedThreadsPublic();
      for (Thread thread : waitingThreads) {
        response.append(thread.getName()).append("<br>");
      }

      return response.toString();
    } else {
      return "No threads are waiting for the lock.";
    }
  }
}
```
### コードの説明
- /cache/lock/{key}エンドポイント

新しいスレッドを作成し、ReentrantLockを40秒間保持します。
この間に他のスレッドがロックを取得しようとすると、待機状態になります。
- /cache/wait/{key}エンドポイント

- hasQueuedThreads()メソッドを使用して、ロック待ちのスレッドが存在するかを確認します
- getQueuedThreadsPublic()メソッドを使用して、待機しているスレッドのリストを取得し、スレッド名を返します
### Spring Bootアプリケーションの起動
Spring Bootアプリケーションを起動し、以下の手順で動作を確認します。

ロックを保持するスレッドを作成

/cache/lock/{key}エンドポイントにアクセスします。
これにより、1つのスレッドがロックを保持し、40秒間ロックを解放しません。

ロック待ちのスレッドを確認

- /cache/wait/{key}エンドポイントにアクセスします
ロック待ちのスレッドが存在する場合、そのスレッド名が表示されます。
動作確認例
/cache/lock/{key}にアクセスすると、以下のようなレスポンスが返ります。

Lock test started. Check /lock/waiting-threads for waiting threads.

- /cache/wait/{key}にアクセスすると、ロック待ちのスレッドが存在する場合、以下のようなレスポンスが返ります

Threads waiting for the lock:
http-nio-8080-exec-3
http-nio-8080-exec-2

ロック待ちのスレッドが存在しない場合は、以下のようなレスポンスが返ります。

No threads are waiting for the lock.

### 注意点
- スレッドの安全性

ReentrantLockを使用する際は、ロックの取得と解放を確実に行うようにしてください（try-finallyブロックを使用）。

- デバッグ用途

getQueuedThreads()メソッドはデバッグやモニタリングの目的で使用することが推奨されます。アプリケーションロジックでの使用は避けるべきです。

- パフォーマンス

ロック待ちのスレッドを頻繁に取得する操作は、パフォーマンスに影響を与える可能性があります。

この方法で、Spring BootのAPIにReentrantLockを組み込み、ロック待ちのスレッドを出力することができます。