Replace synchronized blocks with Locks (#5565)

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 047bba8..3d07d3f 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -56,6 +56,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Supplier;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -92,18 +93,23 @@
         private final MultivaluedMap<TYPE, InstanceContext<?>> singletonInstances = new MultivaluedHashMap<>();
         private final ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
         private final List<Object> threadPredestroyables = Collections.synchronizedList(new LinkedList<>());
+        private final ReentrantLock singletonInstancesLock = new ReentrantLock();
 
         private <T> List<InstanceContext<?>> _getSingletons(TYPE clazz) {
             List<InstanceContext<?>> si;
-            synchronized (singletonInstances) {
+            singletonInstancesLock.lock();
+            try {
                 si = singletonInstances.get(clazz);
+            } finally {
+                singletonInstancesLock.unlock();
             }
             return si;
         }
 
         @SuppressWarnings("unchecked")
         <T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
-            synchronized (singletonInstances) {
+            singletonInstancesLock.lock();
+            try {
                 // check existing singleton with a qualifier already created by another thread io a meantime
                 List<InstanceContext<?>> values = singletonInstances.get(clazz);
                 if (values != null) {
@@ -118,6 +124,8 @@
                 singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
                 threadPredestroyables.add(instance);
                 return instance;
+            } finally {
+                singletonInstancesLock.unlock();
             }
         }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
index cb7545f..77ab1e1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -37,7 +37,9 @@
 import java.util.ResourceBundle;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
@@ -79,6 +81,7 @@
     private final Map<Long, Map<String, Callable<List<Class<?>>>>> factories =
             new HashMap<Long, Map<String, Callable<List<Class<?>>>>>();
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private static final Lock INSTANCE_LOCK = new ReentrantLock();
 
     private static OsgiRegistry instance;
 
@@ -90,16 +93,21 @@
      *
      * @return an {@code OsgiRegistry} instance.
      */
-    public static synchronized OsgiRegistry getInstance() {
-        if (instance == null) {
-            final ClassLoader classLoader = AccessController
-                    .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
-            if (classLoader instanceof BundleReference) {
-                final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
-                if (context != null) { // context could be still null if the current bundle has not been started
-                    instance = new OsgiRegistry(context);
+    public static OsgiRegistry getInstance() {
+        INSTANCE_LOCK.lock();
+        try {
+            if (instance == null) {
+                final ClassLoader classLoader = AccessController
+                        .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
+                if (classLoader instanceof BundleReference) {
+                    final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
+                    if (context != null) { // context could be still null if the current bundle has not been started
+                        instance = new OsgiRegistry(context);
+                    }
                 }
             }
+        } finally {
+            INSTANCE_LOCK.unlock();
         }
         return instance;
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
index 46184d8..03d15d3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -33,6 +33,8 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -797,17 +799,20 @@
     public abstract static class ServiceIteratorProvider {
 
         private static volatile ServiceIteratorProvider sip;
-        private static final Object sipLock = new Object();
+        private static final Lock sipLock = new ReentrantLock();
 
         private static ServiceIteratorProvider getInstance() {
             // TODO: check the following is a good practice: Double-check idiom for lazy initialization of fields.
             ServiceIteratorProvider result = sip;
             if (result == null) { // First check (no locking)
-                synchronized (sipLock) {
+                sipLock.lock();
+                try {
                     result = sip;
                     if (result == null) { // Second check (with locking)
                         sip = result = new DefaultServiceIteratorProvider();
                     }
+                } finally {
+                    sipLock.unlock();
                 }
             }
             return result;
@@ -819,8 +824,11 @@
                 final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
                 security.checkPermission(rp);
             }
-            synchronized (sipLock) {
+            sipLock.lock();
+            try {
                 ServiceIteratorProvider.sip = sip;
+            } finally {
+                sipLock.unlock();
             }
         }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
index 4761644..5c3a747 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,9 @@
 
 package org.glassfish.jersey.internal.util.collection;
 
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
 /**
  * A collection of {@link Value Value provider} factory & utility methods.
  *
@@ -297,25 +300,28 @@
 
     private static class LazyValueImpl<T> implements LazyValue<T> {
 
-        private final Object lock;
+        private final Lock lock;
         private final Value<T> delegate;
 
         private volatile Value<T> value;
 
         public LazyValueImpl(final Value<T> delegate) {
             this.delegate = delegate;
-            this.lock = new Object();
+            this.lock = new ReentrantLock();
         }
 
         @Override
         public T get() {
             Value<T> result = value;
             if (result == null) {
-                synchronized (lock) {
+                lock.lock();
+                try {
                     result = value;
                     if (result == null) {
                         value = result = Values.of(delegate.get());
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
             return result.get();
@@ -380,21 +386,22 @@
 
     private static class LazyUnsafeValueImpl<T, E extends Throwable> implements LazyUnsafeValue<T, E> {
 
-        private final Object lock;
+        private final Lock lock;
         private final UnsafeValue<T, E> delegate;
 
         private volatile UnsafeValue<T, E> value;
 
         public LazyUnsafeValueImpl(final UnsafeValue<T, E> delegate) {
             this.delegate = delegate;
-            this.lock = new Object();
+            this.lock = new ReentrantLock();
         }
 
         @Override
         public T get() throws E {
             UnsafeValue<T, E> result = value;
             if (result == null) {
-                synchronized (lock) {
+                lock.lock();
+                try {
                     result = value;
                     //noinspection ConstantConditions
                     if (result == null) {
@@ -406,6 +413,8 @@
                         }
                         value = result;
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
             return result.get();
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
index 75479e6..0f76eed 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -50,13 +50,7 @@
 
     private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
 
-    private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = new ThreadLocal<List<SimpleDateFormat>>() {
-
-        @Override
-        protected synchronized List<SimpleDateFormat> initialValue() {
-            return createDateFormats();
-        }
-    };
+    private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = ThreadLocal.withInitial(() -> createDateFormats());
 
     private static List<SimpleDateFormat> createDateFormats() {
         final SimpleDateFormat[] formats = new SimpleDateFormat[]{
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index fe5a249..62df83f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -28,6 +28,8 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.core.Cookie;
 import jakarta.ws.rs.core.MediaType;
@@ -622,6 +624,7 @@
 
     private abstract static class ListReader<T> {
         private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+        private final Lock lock = new ReentrantLock();
         protected final ListElementCreator<T> creator;
 
         protected ListReader(ListElementCreator<T> creator) {
@@ -639,7 +642,8 @@
             List<T> list = LIST_CACHE.getIfPresent(header);
 
             if (list == null) {
-                synchronized (LIST_CACHE) {
+                lock.lock();
+                try {
                     list = LIST_CACHE.getIfPresent(header);
                     if (list == null) {
                         HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
@@ -655,6 +659,8 @@
                         }
                         LIST_CACHE.put(header, list);
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
index 91738c1..7a1a4a5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,6 +22,8 @@
 import java.lang.reflect.Type;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
@@ -252,6 +254,7 @@
 
         private final InputStream original;
         private final MessageBodyReader reader;
+        private final Lock markLock = new ReentrantLock();
 
         private UnCloseableInputStream(final InputStream original, final MessageBodyReader reader) {
             this.original = original;
@@ -284,13 +287,23 @@
         }
 
         @Override
-        public synchronized void mark(final int i) {
-            original.mark(i);
+        public void mark(final int i) {
+            markLock.lock();
+            try {
+                original.mark(i);
+            } finally {
+                markLock.unlock();
+            }
         }
 
         @Override
-        public synchronized void reset() throws IOException {
-            original.reset();
+        public void reset() throws IOException {
+            markLock.lock();
+            try {
+                original.reset();
+            } finally {
+                markLock.unlock();
+            }
         }
 
         @Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
index ced41cd..4d92447 100644
--- a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
+++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -244,6 +244,7 @@
      */
     protected void release(RequestContext context) {
         context.release();
+        currentRequestContext.remove();
     }
 
     /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
index 2ce0542..b671e68 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,6 +26,8 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.container.ConnectionCallback;
 import jakarta.ws.rs.core.GenericType;
@@ -54,7 +56,7 @@
     private final BlockingDeque<T> queue = new LinkedBlockingDeque<>();
     private final byte[] chunkDelimiter;
     private final AtomicBoolean resumed = new AtomicBoolean(false);
-    private final Object lock = new Object();
+    private final Lock lock = new ReentrantLock();
 
     // the following flushing and touchingEntityStream variables are used in a synchronized block exclusively
     private boolean flushing = false;
@@ -202,7 +204,8 @@
                     boolean shouldClose;
                     T t;
 
-                    synchronized (lock) {
+                    lock.lock();
+                    try {
                         if (flushing) {
                             // if another thread is already flushing the queue, we don't have to do anything
                             return null;
@@ -220,13 +223,15 @@
                             // and they don't have to bother
                             flushing = true;
                         }
+                    } finally {
+                        lock.unlock();
                     }
 
                     while (t != null) {
                         try {
-                            synchronized (lock) {
-                                touchingEntityStream = true;
-                            }
+                            lock.lock();
+                            touchingEntityStream = true;
+                            lock.unlock();
 
                             final OutputStream origStream = responseContext.getEntityStream();
                             final OutputStream writtenStream = requestContext.getWorkers().writeTo(
@@ -265,14 +270,15 @@
                             }
                             throw mpe;
                         } finally {
-                           synchronized (lock) {
-                               touchingEntityStream = false;
-                           }
+                           lock.lock();
+                           touchingEntityStream = false;
+                           lock.unlock();
                         }
 
                         t = queue.poll();
                         if (t == null) {
-                            synchronized (lock) {
+                            lock.lock();
+                            try {
                                 // queue seems empty
                                 // check again in the synchronized block before clearing the flushing flag
                                 // first remember the closed flag (this has to be before polling the queue,
@@ -290,6 +296,8 @@
                                     flushing = shouldClose;
                                     break;
                                 }
+                            } finally {
+                                lock.unlock();
                             }
                         }
                     }
@@ -303,18 +311,20 @@
             onClose(e);
         } finally {
             if (closed) {
+                lock.lock();
                 try {
-                    synchronized (lock) {
-                        if (!touchingEntityStream) {
-                            responseContext.close();
-                        } // else the next thread will close responseContext
-                    }
+                    if (!touchingEntityStream) {
+                        responseContext.close();
+                    } // else the next thread will close responseContext
                 } catch (final Exception e) {
                     // if no exception remembered before, remember this one
                     // otherwise the previously remembered exception (from catch clause) takes precedence
                     ex = ex == null ? e : ex;
+                } finally {
+                    lock.unlock();
                 }
 
+
                 requestScopeContext.release();
 
                 // rethrow remembered exception (if any)
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
index 6f74427..d6eb5a6 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
@@ -34,6 +34,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -800,7 +802,7 @@
             }
         };
 
-        private final Object stateLock = new Object();
+        private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
         private State state = RUNNING;
         private boolean cancelled = false;
 
@@ -832,11 +834,13 @@
         @Override
         public void onTimeout(final ContainerResponseWriter responseWriter) {
             final TimeoutHandler handler = timeoutHandler;
+            stateLock.readLock().lock();
             try {
-                synchronized (stateLock) {
-                    if (state == SUSPENDED) {
-                        handler.handleTimeout(this);
-                    }
+                if (state == SUSPENDED) {
+                    stateLock.readLock().unlock(); // unlock before handleTimeout to prevent write lock in handleTimeout
+                    handler.handleTimeout(this);
+                } else {
+                    stateLock.readLock().unlock();
                 }
             } catch (final Throwable throwable) {
                 resume(throwable);
@@ -845,9 +849,9 @@
 
         @Override
         public void onComplete(final Throwable throwable) {
-            synchronized (stateLock) {
-                state = COMPLETED;
-            }
+            stateLock.writeLock().lock();
+            state = COMPLETED;
+            stateLock.writeLock().unlock();
         }
 
         @Override
@@ -875,14 +879,19 @@
 
         @Override
         public boolean suspend() {
-            synchronized (stateLock) {
-                if (state == RUNNING) {
-                    if (responder.processingContext.request().getResponseWriter().suspend(
-                            AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
-                        state = SUSPENDED;
-                        return true;
-                    }
+            stateLock.readLock().lock();
+            if (state == RUNNING) {
+                stateLock.readLock().unlock();
+                if (responder.processingContext.request().getResponseWriter().suspend(
+                        AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
+                    // Must release read lock before acquiring write lock
+                    stateLock.writeLock().lock();
+                    state = SUSPENDED;
+                    stateLock.writeLock().unlock();
+                    return true;
                 }
+            } else {
+                stateLock.readLock().unlock();
             }
             return false;
         }
@@ -925,12 +934,17 @@
         }
 
         private boolean resume(final Runnable handler) {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 if (state != SUSPENDED) {
                     return false;
                 }
-                state = RESUMED;
+            } finally {
+                stateLock.readLock().unlock();
             }
+            stateLock.writeLock().lock();
+            state = RESUMED;
+            stateLock.writeLock().unlock();
 
             try {
                 responder.runtime.requestScope.runInScope(requestContext, handler);
@@ -978,7 +992,8 @@
         }
 
         private boolean cancel(final Value<Response> responseValue) {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 if (cancelled) {
                     return true;
                 }
@@ -986,10 +1001,15 @@
                 if (state != SUSPENDED) {
                     return false;
                 }
-                state = RESUMED;
-                cancelled = true;
+            } finally {
+                stateLock.readLock().unlock();
             }
 
+            stateLock.writeLock().lock();
+            state = RESUMED;
+            cancelled = true;
+            stateLock.writeLock().unlock();
+
             responder.runtime.requestScope.runInScope(requestContext, new Runnable() {
                 @Override
                 public void run() {
@@ -1006,29 +1026,41 @@
         }
 
         public boolean isRunning() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == RUNNING;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isSuspended() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == SUSPENDED;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isCancelled() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return cancelled;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isDone() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == COMPLETED;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
index 761065e..91ec565 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -42,7 +44,7 @@
     private ScheduledFuture<?> timeoutTask = null; // guarded by runtimeLock
     private ContainerResponseWriter.TimeoutHandler timeoutHandler = null; // guarded by runtimeLock
     private boolean suspended = false; // guarded by runtimeLock
-    private final Object runtimeLock = new Object();
+    private final Lock runtimeLock = new ReentrantLock();
 
     private final ContainerResponseWriter containerResponseWriter;
     private final ScheduledExecutorService executor;
@@ -71,7 +73,8 @@
      * @see ContainerResponseWriter#suspend(long, TimeUnit, ContainerResponseWriter.TimeoutHandler)
      */
     public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHandler handler) {
-        synchronized (runtimeLock) {
+        runtimeLock.lock();
+        try {
             if (suspended) {
                 return false;
             }
@@ -81,6 +84,8 @@
 
             containerResponseWriter.setSuspendTimeout(timeOut, unit);
             return true;
+        } finally {
+            runtimeLock.unlock();
         }
     }
 
@@ -94,7 +99,8 @@
      * @see ContainerResponseWriter#setSuspendTimeout(long, TimeUnit)
      */
     public void setSuspendTimeout(final long timeOut, final TimeUnit unit) throws IllegalStateException {
-        synchronized (runtimeLock) {
+        runtimeLock.lock();
+        try {
             if (!suspended) {
                 throw new IllegalStateException(LocalizationMessages.SUSPEND_NOT_SUSPENDED());
             }
@@ -110,18 +116,21 @@
 
                     @Override
                     public void run() {
+                        runtimeLock.lock();
                         try {
-                            synchronized (runtimeLock) {
-                                timeoutHandler.onTimeout(containerResponseWriter);
-                            }
+                            timeoutHandler.onTimeout(containerResponseWriter);
                         } catch (final Throwable throwable) {
                             LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_HANDLER_EXECUTION_FAILED(), throwable);
+                        } finally {
+                            runtimeLock.unlock();
                         }
                     }
                 }, timeOut, unit);
             } catch (final IllegalStateException ex) {
                 LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_SCHEDULING_ERROR(), ex);
             }
+        } finally {
+            runtimeLock.unlock();
         }
     }
 
@@ -132,10 +141,15 @@
         close(false);
     }
 
-    private synchronized void close(final boolean interruptIfRunning) {
-        if (timeoutTask != null) {
-            timeoutTask.cancel(interruptIfRunning);
-            timeoutTask = null;
+    private void close(final boolean interruptIfRunning) {
+        runtimeLock.lock();
+        try {
+            if (timeoutTask != null) {
+                timeoutTask.cancel(interruptIfRunning);
+                timeoutTask = null;
+            }
+        } finally {
+            runtimeLock.unlock();
         }
     }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
index f8abbed..4e95a47 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
 import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.logging.Level;
@@ -54,7 +56,7 @@
     private final Consumer<Binding> registerBinding;
 
     private final Set<Class<?>> bindingCache;
-    private final Object bindingCacheLock;
+    private final Lock bindingCacheLock;
 
     private volatile ResourceModel resourceModel;
 
@@ -73,7 +75,7 @@
         this.injectInstance = injectInstance;
         this.registerBinding = registerBinding;
         this.bindingCache = Collections.newSetFromMap(new IdentityHashMap<>());
-        this.bindingCacheLock = new Object();
+        this.bindingCacheLock = new ReentrantLock();
     }
 
     @Override
@@ -110,11 +112,14 @@
             return;
         }
 
-        synchronized (bindingCacheLock) {
+        bindingCacheLock.lock();
+        try {
             if (bindingCache.contains(resourceClass)) {
                 return;
             }
             unsafeBindResource(resourceClass, null);
+        } finally {
+            bindingCacheLock.unlock();
         }
     }
 
@@ -135,7 +140,8 @@
             return;
         }
 
-        synchronized (bindingCacheLock) {
+        bindingCacheLock.lock();
+        try {
             if (bindingCache.contains(resourceClass)) {
                 return;
             }
@@ -144,6 +150,8 @@
             }
 
             bindingCache.add(resourceClass);
+        } finally {
+            bindingCacheLock.unlock();
         }
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
index a7d5829..dc99a5d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -60,7 +62,7 @@
     private volatile ResourcesMBeanGroup resourceClassStatsGroup;
     private volatile ExceptionMapperMXBeanImpl exceptionMapperMXBean;
     private final AtomicBoolean destroyed = new AtomicBoolean(false);
-    private final Object LOCK = new Object();
+    private final Lock LOCK = new ReentrantLock();
 
     /**
      * Name of domain that will prefix mbeans {@link ObjectName}. The code uses this
@@ -110,45 +112,47 @@
     void registerMBean(Object mbean, String namePostfix) {
         final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
         final String name = domain + namePostfix;
+        LOCK.lock();
         try {
-            synchronized (LOCK) {
-                if (destroyed.get()) {
-                    // already destroyed
-                    return;
-                }
-                final ObjectName objectName = new ObjectName(name);
-                if (mBeanServer.isRegistered(objectName)) {
-
-                    LOGGER.log(Level.WARNING,
-                            LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
-                    mBeanServer.unregisterMBean(objectName);
-                }
-                mBeanServer.registerMBean(mbean, objectName);
+            if (destroyed.get()) {
+                // already destroyed
+                return;
             }
+            final ObjectName objectName = new ObjectName(name);
+            if (mBeanServer.isRegistered(objectName)) {
+
+                LOGGER.log(Level.WARNING,
+                        LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
+                mBeanServer.unregisterMBean(objectName);
+            }
+            mBeanServer.registerMBean(mbean, objectName);
         } catch (JMException e) {
             throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_REGISTRATION(name), e);
+        } finally {
+            LOCK.unlock();
         }
     }
 
     private void unregisterJerseyMBeans(boolean destroy) {
         final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+        LOCK.lock();
         try {
-            synchronized (LOCK) {
-                if (destroy) {
-                    destroyed.set(true); // do not register new beans since now
-                }
+            if (destroy) {
+                destroyed.set(true); // do not register new beans since now
+            }
 
-                if (domain == null) {
-                    // No bean has been registered yet.
-                    return;
-                }
-                final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
-                for (ObjectName name : names) {
-                    mBeanServer.unregisterMBean(name);
-                }
+            if (domain == null) {
+                // No bean has been registered yet.
+                return;
+            }
+            final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
+            for (ObjectName name : names) {
+                mBeanServer.unregisterMBean(name);
             }
         } catch (Exception e) {
             throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_UNREGISTRATION_DESTROY(), e);
+        } finally {
+            LOCK.unlock();
         }
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
index 14e86e3..818bdbe 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.NoSuchElementException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.logging.Level;
@@ -110,6 +112,7 @@
     public InputStream open() {
         //noinspection NullableProblems
         return new InputStream() {
+            private final Lock markLock = new ReentrantLock();
 
             @Override
             public int read() throws IOException {
@@ -142,13 +145,23 @@
             }
 
             @Override
-            public synchronized void mark(final int i) {
-                jarInputStream.mark(i);
+            public void mark(final int i) {
+                markLock.lock();
+                try {
+                    jarInputStream.mark(i);
+                } finally {
+                    markLock.unlock();
+                }
             }
 
             @Override
-            public synchronized void reset() throws IOException {
-                jarInputStream.reset();
+            public void reset() throws IOException {
+                markLock.lock();
+                try {
+                    jarInputStream.reset();
+                } finally {
+                    markLock.unlock();
+                }
             }
 
             @Override
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
index 06a9432..d482dde 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,6 +27,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.glassfish.jersey.internal.OsgiRegistry;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
@@ -54,7 +56,7 @@
  * URI schemes.
  * <p>
  * Further schemes may be registered by registering an implementation of
- * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is the
+ * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is
  * the fully qualified class name of {@link UriSchemeResourceFinderFactory}.
  * <p>
  * If a URI scheme is not supported a {@link ResourceFinderException} will be thrown
@@ -195,13 +197,15 @@
     public abstract static class ResourcesProvider {
 
         private static volatile ResourcesProvider provider;
+        private static final Lock RESOURCE_PROVIDER_LOCK = new ReentrantLock();
 
         private static ResourcesProvider getInstance() {
             // Double-check idiom for lazy initialization
             ResourcesProvider result = provider;
 
             if (result == null) { // first check without locking
-                synchronized (ResourcesProvider.class) {
+                RESOURCE_PROVIDER_LOCK.lock();
+                try {
                     result = provider;
                     if (result == null) { // second check with locking
                         provider = result = new ResourcesProvider() {
@@ -214,6 +218,8 @@
                         };
 
                     }
+                } finally {
+                    RESOURCE_PROVIDER_LOCK.unlock();
                 }
 
             }
@@ -226,9 +232,9 @@
                 final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
                 security.checkPermission(rp);
             }
-            synchronized (ResourcesProvider.class) {
-                ResourcesProvider.provider = provider;
-            }
+            RESOURCE_PROVIDER_LOCK.lock();
+            ResourcesProvider.provider = provider;
+            RESOURCE_PROVIDER_LOCK.unlock();
         }
 
         /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
index 5a7188a..982a44b 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
 import java.net.URI;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
@@ -56,6 +58,7 @@
 
     private byte[] wadlXmlRepresentation;
     private String lastModified;
+    private final Lock lock = new ReentrantLock();
 
     @Context
     private WadlApplicationContext wadlContext;
@@ -71,7 +74,8 @@
 
     @Produces({"application/vnd.sun.wadl+xml", "application/xml"})
     @GET
-    public synchronized Response getWadl(@Context UriInfo uriInfo) {
+    public Response getWadl(@Context UriInfo uriInfo) {
+        lock.lock();
         try {
             if (!wadlContext.isWadlGenerationEnabled()) {
                 return Response.status(Response.Status.NOT_FOUND).build();
@@ -103,6 +107,8 @@
             return Response.ok(new ByteArrayInputStream(wadlXmlRepresentation)).header("Last-modified", lastModified).build();
         } catch (Exception e) {
             throw new ProcessingException("Error generating /application.wadl.", e);
+        } finally {
+            lock.unlock();
         }
     }
 
@@ -110,9 +116,10 @@
     @Produces({"application/xml"})
     @GET
     @Path("{path}")
-    public synchronized Response getExternalGrammar(
+    public Response getExternalGrammar(
             @Context UriInfo uriInfo,
             @PathParam("path") String path) {
+        lock.lock();
         try {
             // Fail if wadl generation is disabled
             if (!wadlContext.isWadlGenerationEnabled()) {
@@ -135,6 +142,8 @@
                     .build();
         } catch (Exception e) {
             throw new ProcessingException(LocalizationMessages.ERROR_WADL_RESOURCE_EXTERNAL_GRAMMAR(), e);
+        } finally {
+            lock.unlock();
         }
     }
 
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
index 1d3c5c5..0b95efd 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
 import java.lang.ref.WeakReference;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -57,6 +59,7 @@
 
     private static final Map<Class<?>, WeakReference<JAXBContext>> jaxbContexts =
             new WeakHashMap<Class<?>, WeakReference<JAXBContext>>();
+    private static final Lock jaxbContextsLock = new ReentrantLock();
     private final Providers jaxrsProviders;
     private final boolean fixedResolverMediaType;
     private final Value<ContextResolver<JAXBContext>> mtContext;
@@ -149,7 +152,7 @@
     /**
      * Get the JAXB unmarshaller for the given class and media type.
      * <p>
-     * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+     * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
      * created with a fixed resolver media type}, the supplied media type argument will be ignored.
      * </p>
      *
@@ -192,7 +195,7 @@
     /**
      * Get the JAXB marshaller for the given class and media type.
      * <p>
-     * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+     * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
      * created with a fixed resolver media type}, the supplied media type argument will be ignored.
      * </p>
      *
@@ -280,7 +283,8 @@
      * @throws JAXBException in case the JAXB context retrieval fails.
      */
     protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException {
-        synchronized (jaxbContexts) {
+        jaxbContextsLock.lock();
+        try {
             final WeakReference<JAXBContext> ref = jaxbContexts.get(type);
             JAXBContext c = (ref != null) ? ref.get() : null;
             if (c == null) {
@@ -288,7 +292,10 @@
                 jaxbContexts.put(type, new WeakReference<JAXBContext>(c));
             }
             return c;
+        } finally {
+            jaxbContextsLock.unlock();
         }
+
     }
 
     /**
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
index e7e399d..7065c43 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,6 +20,8 @@
 import java.lang.reflect.Type;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.core.Context;
@@ -54,6 +56,7 @@
 public class JaxbStringReaderProvider {
 
     private static final Map<Class, JAXBContext> jaxbContexts = new WeakHashMap<Class, JAXBContext>();
+    private static final Lock jaxbContextsLock = new ReentrantLock();
     private final Value<ContextResolver<JAXBContext>> mtContext;
     private final Value<ContextResolver<Unmarshaller>> mtUnmarshaller;
 
@@ -116,13 +119,16 @@
      * @throws JAXBException in case JAXB context retrieval fails.
      */
     protected JAXBContext getStoredJAXBContext(Class type) throws JAXBException {
-        synchronized (jaxbContexts) {
+        jaxbContextsLock.lock();
+        try {
             JAXBContext c = jaxbContexts.get(type);
             if (c == null) {
                 c = JAXBContext.newInstance(type);
                 jaxbContexts.put(type, c);
             }
             return c;
+        } finally {
+            jaxbContextsLock.unlock();
         }
     }