### API(s)

```java
com.google.common.cache.LocalCache$Segment.lockedGetOrLoad
```


### How do you want it to be improved?

[This load](https://github.com/google/guava/blob/4f12c5891a7adedbaa1d99fc9f77d8cc4e9da206/guava/src/com/google/common/cache/LocalCache.java#L2191C1-L2191C71) should be possible without using `synchronized`.

### Why do we need it to be improved?

Using synchronized here is not performant when the current Thread is a Java 21 `VirtualThread` and the loader does some blocking operation, because the `synchronized` prevents the JVM from unmounting the Thread. The performance could be drastically improved by using p.e. `ReentrantLock`.

### Example

```java
Thread.ofVirtual().start( () -> {
  Database database;
  var databaseLoader = new CacheLoader<String, Object>() {
        @Override
        public Object load(String key) {
            return database.load(key); // loads an object by querying a database over the network 
        }
    };
var cache = CacheBuilder.newBuilder().build(databaseLoader);
var fooObject = cache.getOrLoad("foo"); // cache miss, invokes the loader
});
```


### Current Behavior

In the above example, the loader is invoked. This invocation happens inside a `synchronized` block in `com.google.common.cache.LocalCache$Segment.lockedGetOrLoad`. Because the loader does I/O but is inside the `synchronized` block the `VirtualThread` can not be unmounted and therefore blocks its carrier. This is bad for the throughput of the application. This behavior can be observed  using any loader that does a blocking operation when it is invoked from a `VirtualThread` (just start your JVM with `-Djdk.tracePinnedThreads=full`). The performance impact is dependent on how long the loader takes, but since generally one only caches things that are expensive to do I believe this to be a problem for pretty much anyone using `LocalCache` in combination with `VirtualThreads`. 

### Desired Behavior

`com.google.common.cache.LocalCache$Segment.lockedGetOrLoad` should work gracefully with VirtualThreads and not block the carrier Thread if a loader does I/O or some other blocking operation.

The method should therefore be refactored to lock using some other method, p.e. a `ReentrantLock`.

### Concrete Use Cases

We use a `LocalCache` to cache frequently used database objects. Our loader therefore executes a SQL query over the network, which is a blocking operation and takes comparatively long. We are currently experimenting with using `VirtualThreads` in order to improve the throughput of I/O heavy workloads.

### Checklist

- [X] I agree to follow the [code of conduct](https://github.com/google/.github/blob/master/CODE_OF_CONDUCT.md).

- [X] I have read and understood the [contribution guidelines](https://github.com/google/guava/wiki/HowToContribute#feature-requests).

- [X] I have read and understood [Guava's philosophy](https://github.com/google/guava/wiki/PhilosophyExplained), and I strongly believe that this proposal aligns with it.

public class LocalCacheTest extends TestCase {
  private static class TestStringCacheGenerator extends TestStringMapGenerator {
    private final CacheBuilder<? super String, ? super String> builder;
    TestStringCacheGenerator(CacheBuilder<? super String, ? super String> builder) {
      this.builder = builder;
    }
    @Override
    protected Map<String, String> create(Entry<String, String>[] entries) {
      LocalCache<String, String> map = makeLocalCache(builder);
      for (Entry<String, String> entry : entries) {
        map.put(entry.getKey(), entry.getValue());
      }
      return map;
    }
  }
  public static Test suite() {
    TestSuite suite = new TestSuite();
    suite.addTestSuite(LocalCacheTest.class);