From 14181932ec6bfc8822e4c9b31f684349f7fe4c49 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 16 Oct 2020 00:20:42 +1300 Subject: [PATCH] Refactor synchronized to ReentrantLock (#2081) --- .../src/main/java/io/ebean/DbContext.java | 25 +++---- .../java/io/ebean/EbeanServerFactory.java | 10 +-- .../main/java/io/ebean/bean/BeanLoader.java | 7 ++ .../io/ebean/bean/EntityBeanIntercept.java | 16 +++- .../java/io/ebean/bean/SingleBeanLoader.java | 25 +++++++ .../ebean/common/AbstractBeanCollection.java | 3 + .../main/java/io/ebean/common/BeanList.java | 10 ++- .../main/java/io/ebean/common/BeanMap.java | 10 ++- .../main/java/io/ebean/common/BeanSet.java | 10 ++- .../io/ebean/common/CopyOnFirstWriteList.java | 15 ++-- .../dbplatform/SequenceIdGenerator.java | 34 +++++---- .../java/io/ebean/event/ShutdownManager.java | 65 +++++++++------- .../main/java/io/ebean/plugin/SpiServer.java | 7 +- .../server/cache/DefaultCacheHolder.java | 11 +-- .../server/cluster/ClusterManager.java | 13 +++- .../server/core/DefaultContainer.java | 7 +- .../server/core/DefaultServer.java | 74 +++++-------------- .../server/core/InternString.java | 9 ++- .../server/idgen/UuidV1RndIdGenerator.java | 8 +- .../server/lib/DaemonExecutorService.java | 8 +- .../server/lib/DaemonScheduleThreadPool.java | 8 +- .../server/loadcontext/DLoadBaseContext.java | 4 + .../server/loadcontext/DLoadBeanContext.java | 17 ++++- .../server/loadcontext/DLoadManyContext.java | 13 +++- .../io/ebeaninternal/server/query/CQuery.java | 20 +++-- .../server/query/CQueryBindCapture.java | 8 +- .../server/query/LimitOffsetPagedList.java | 26 ++++--- .../server/querydefn/DefaultOrmQuery.java | 18 ++++- .../DefaultPersistenceContext.java | 59 +++++++++++---- .../transaction/DefaultProfileHandler.java | 13 +++- 30 files changed, 368 insertions(+), 185 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/DbContext.java b/ebean-api/src/main/java/io/ebean/DbContext.java index f7af636bf..cf09dc7fb 100644 --- a/ebean-api/src/main/java/io/ebean/DbContext.java +++ b/ebean-api/src/main/java/io/ebean/DbContext.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** * Holds Database instances. @@ -22,17 +23,11 @@ final class DbContext { private static final DbContext INSTANCE = new DbContext(); - /** - * Cache for fast concurrent read access. - */ private final ConcurrentHashMap concMap = new ConcurrentHashMap<>(); - /** - * Cache for synchronized read, creation and put. Protected by the monitor object. - */ private final HashMap syncMap = new HashMap<>(); - private final Object monitor = new Object(); + private final ReentrantLock lock = new ReentrantLock(false); /** * The 'default' Database. @@ -91,27 +86,28 @@ final class DbContext { if (name == null || name.isEmpty()) { return defaultDatabase; } - // non-synchronized read Database server = concMap.get(name); if (server != null) { return server; } - // synchronized read, create and put return getWithCreate(name); } /** - * Synchronized read, create and put of Databases. + * Read, create and put of Databases. */ private Database getWithCreate(String name) { - synchronized (monitor) { + lock.lock(); + try { Database server = syncMap.get(name); if (server == null) { // register when creating server this way - server = EbeanServerFactory.create(name); + server = DatabaseFactory.create(name); register(server, false); } return server; + } finally { + lock.unlock(); } } @@ -123,12 +119,15 @@ final class DbContext { } private void registerWithName(String name, Database server, boolean isDefault) { - synchronized (monitor) { + lock.lock(); + try { concMap.put(name, server); syncMap.put(name, server); if (isDefault) { defaultDatabase = server; } + } finally { + lock.unlock(); } } diff --git a/ebean-api/src/main/java/io/ebean/EbeanServerFactory.java b/ebean-api/src/main/java/io/ebean/EbeanServerFactory.java index ed98effd6..a38b6aed9 100644 --- a/ebean-api/src/main/java/io/ebean/EbeanServerFactory.java +++ b/ebean-api/src/main/java/io/ebean/EbeanServerFactory.java @@ -39,28 +39,28 @@ public class EbeanServerFactory { * Call this prior to creating any EbeanServer instances or alternatively set the * ContainerConfig on the ServerConfig when creating the first EbeanServer instance. */ - public static synchronized void initialiseContainer(ContainerConfig containerConfig) { + public static void initialiseContainer(ContainerConfig containerConfig) { DatabaseFactory.initialiseContainer(containerConfig); } /** * Create using ebean.properties to configure the database. */ - public static synchronized EbeanServer create(String name) { + public static EbeanServer create(String name) { return (EbeanServer)DatabaseFactory.create(name); } /** * Create using the ServerConfig object to configure the database. */ - public static synchronized EbeanServer create(ServerConfig config) { + public static EbeanServer create(ServerConfig config) { return (EbeanServer)DatabaseFactory.create(config); } /** * Create using the ServerConfig additionally specifying a classLoader to use as the context class loader. */ - public static synchronized EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) { + public static EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) { return (EbeanServer)DatabaseFactory.createWithContextClassLoader(config, classLoader); } @@ -70,7 +70,7 @@ public class EbeanServerFactory { * This is typically invoked via JVM shutdown hook and not explicitly called. *

*/ - public static synchronized void shutdown() { + public static void shutdown() { DatabaseFactory.shutdown(); } diff --git a/ebean-api/src/main/java/io/ebean/bean/BeanLoader.java b/ebean-api/src/main/java/io/ebean/bean/BeanLoader.java index af96a3d01..5bfbfda08 100644 --- a/ebean-api/src/main/java/io/ebean/bean/BeanLoader.java +++ b/ebean-api/src/main/java/io/ebean/bean/BeanLoader.java @@ -1,5 +1,7 @@ package io.ebean.bean; +import java.util.concurrent.locks.Lock; + /** * Loads a entity bean. *

@@ -18,4 +20,9 @@ public interface BeanLoader { */ void loadBean(EntityBeanIntercept ebi); + /** + * Obtain a lock on the loader. + */ + Lock lock(); + } diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 090e93149..4bb520a32 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -14,6 +14,8 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * This is the object added to every entity bean using byte code enhancement. @@ -30,6 +32,8 @@ public final class EntityBeanIntercept implements Serializable { private static final int STATE_REFERENCE = 1; private static final int STATE_LOADED = 2; + private transient final ReentrantLock lock = new ReentrantLock(false); + private transient NodeUsageCollector nodeUsageCollector; private transient PersistenceContext persistenceContext; @@ -803,7 +807,8 @@ public final class EntityBeanIntercept implements Serializable { * Load the bean when it is a reference. */ protected void loadBean(int loadProperty) { - synchronized (this) { + lock.lock(); + try { if (beanLoader == null) { final Database database = DB.byName(ebeanServerName); if (database == null) { @@ -811,14 +816,19 @@ public final class EntityBeanIntercept implements Serializable { } // For stand alone reference bean or after deserialisation lazy load // using the ebeanServer. Synchronise only on the bean. - loadBeanInternal(loadProperty, database.getPluginApi()); + loadBeanInternal(loadProperty, database.getPluginApi().beanLoader()); return; } + } finally { + lock.unlock(); } - synchronized (beanLoader) { + final Lock lock = beanLoader.lock(); + try { // Lazy loading using LoadBeanContext which supports batch loading // Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree') loadBeanInternal(loadProperty, beanLoader); + } finally { + lock.unlock(); } } diff --git a/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java b/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java index fd4ce6651..4d84ada39 100644 --- a/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java +++ b/ebean-api/src/main/java/io/ebean/bean/SingleBeanLoader.java @@ -2,11 +2,16 @@ package io.ebean.bean; import io.ebean.Database; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * BeanLoader used when single beans are loaded (which is usually not ideal / N+1). */ public abstract class SingleBeanLoader implements BeanLoader { + private final ReentrantLock lock = new ReentrantLock(false); + protected final Database database; SingleBeanLoader(Database database) { @@ -18,6 +23,12 @@ public abstract class SingleBeanLoader implements BeanLoader { return database.getName(); } + @Override + public Lock lock() { + lock.lock(); + return lock; + } + /** * Single bean lazy loaded when bean from L2 cache. */ @@ -45,4 +56,18 @@ public abstract class SingleBeanLoader implements BeanLoader { database.getPluginApi().loadBeanRef(ebi); } } + + /** + * Single bean lazy loaded when a reference bean. + */ + public static class Dflt extends SingleBeanLoader { + public Dflt(Database database) { + super(database); + } + + @Override + public void loadBean(EntityBeanIntercept ebi) { + database.getPluginApi().loadBean(ebi); + } + } } diff --git a/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java b/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java index 5790f9d13..297c6fb9d 100644 --- a/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java +++ b/ebean-api/src/main/java/io/ebean/common/AbstractBeanCollection.java @@ -7,6 +7,7 @@ import io.ebean.bean.BeanCollectionLoader; import io.ebean.bean.EntityBean; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; /** * Base class for List Set and Map implementations of BeanCollection. @@ -15,6 +16,8 @@ abstract class AbstractBeanCollection implements BeanCollection { private static final long serialVersionUID = 3365725236140187588L; + protected final ReentrantLock lock = new ReentrantLock(false); + protected boolean readOnly; protected boolean disableLazyLoad; diff --git a/ebean-api/src/main/java/io/ebean/common/BeanList.java b/ebean-api/src/main/java/io/ebean/common/BeanList.java index 42d67b993..0aee7f211 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanList.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanList.java @@ -115,7 +115,8 @@ public final class BeanList extends AbstractBeanCollection implements List } private void initClear() { - synchronized (this) { + lock.lock(); + try { if (list == null) { if (!disableLazyLoad && modifyListening) { lazyLoadCollection(true); @@ -123,11 +124,14 @@ public final class BeanList extends AbstractBeanCollection implements List list = new ArrayList<>(); } } + } finally { + lock.unlock(); } } private void init() { - synchronized (this) { + lock.lock(); + try { if (list == null) { if (disableLazyLoad) { list = new ArrayList<>(); @@ -135,6 +139,8 @@ public final class BeanList extends AbstractBeanCollection implements List lazyLoadCollection(false); } } + } finally { + lock.unlock(); } } diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java index 8277223c0..b2ded02c9 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java @@ -121,7 +121,8 @@ public final class BeanMap extends AbstractBeanCollection implements Ma } private void initClear() { - synchronized (this) { + lock.lock(); + try { if (map == null) { if (!disableLazyLoad && modifyListening) { lazyLoadCollection(true); @@ -129,11 +130,14 @@ public final class BeanMap extends AbstractBeanCollection implements Ma map = new LinkedHashMap<>(); } } + } finally { + lock.unlock(); } } private void init() { - synchronized (this) { + lock.lock(); + try { if (map == null) { if (disableLazyLoad) { map = new LinkedHashMap<>(); @@ -141,6 +145,8 @@ public final class BeanMap extends AbstractBeanCollection implements Ma lazyLoadCollection(false); } } + } finally { + lock.unlock(); } } diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java index 42872ad10..ec4dc3829 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java @@ -114,7 +114,8 @@ public final class BeanSet extends AbstractBeanCollection implements Set extends AbstractBeanCollection implements Set(); } } + } finally { + lock.unlock(); } } private void init() { - synchronized (this) { + lock.lock(); + try { if (set == null) { if (disableLazyLoad) { set = new LinkedHashSet<>(); @@ -134,6 +138,8 @@ public final class BeanSet extends AbstractBeanCollection implements Set extends AbstractList implements List, Serializable { private static final long serialVersionUID = 1L; + private final ReentrantLock lock = new ReentrantLock(false); + /** * The underlying List implementation. */ private List list; - - + public CopyOnFirstWriteList(List list) { super(); this.list = list; @@ -166,14 +168,17 @@ public final class CopyOnFirstWriteList extends AbstractList implements Li public int lastIndexOf(Object o) { return list.lastIndexOf(o); } - + private void checkCopyOnWrite() { if (!copied) { - synchronized (this) { + lock.lock(); + try { if (!copied) { list = new ArrayList<>(list); copied = true; } + } finally { + lock.unlock(); } } } diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java index 703fa7958..4ac975944 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.List; import java.util.NavigableSet; import java.util.TreeSet; +import java.util.concurrent.locks.ReentrantLock; /** * Database sequence based IdGenerator. @@ -24,15 +25,9 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { protected static final Logger logger = LoggerFactory.getLogger("io.ebean.SEQ"); - /** - * Used to synchronise the idList access. - */ - protected final Object monitor = new Object(); + private final ReentrantLock lock = new ReentrantLock(false); - /** - * Used to synchronise background loading (loadBatchInBackground). - */ - protected final Object backgroundLoadMonitor = new Object(); + private final ReentrantLock loadLock = new ReentrantLock(false); /** * The actual sequence name. @@ -97,7 +92,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { */ @Override public Object nextId(Transaction t) { - synchronized (monitor) { + lock.lock(); + try { int size = idList.size(); if (size > 0) { maybeLoadMoreInBackground(size); @@ -105,6 +101,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { loadMore(allocationSize); } return idList.pollFirst(); + } finally { + lock.unlock(); } } @@ -118,8 +116,11 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { private void loadMore(int requestSize) { List newIds = getMoreIds(requestSize); - synchronized (monitor) { + lock.lock(); + try { idList.addAll(newIds); + } finally { + lock.unlock(); } } @@ -127,23 +128,26 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { * Load another batch of Id's using a background thread. */ protected void loadInBackground(final int requestSize) { - // single threaded processing... - synchronized (backgroundLoadMonitor) { + loadLock.lock(); + try { if (currentlyBackgroundLoading) { // skip as already background loading logger.debug("... skip background sequence load (another load in progress)"); return; } - currentlyBackgroundLoading = true; - backgroundExecutor.execute(() -> { loadMore(requestSize); - synchronized (backgroundLoadMonitor) { + loadLock.lock(); + try { currentlyBackgroundLoading = false; + } finally { + loadLock.unlock(); } }); + } finally { + loadLock.lock(); } } diff --git a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java index 32ef7b846..f01a22b83 100644 --- a/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java +++ b/ebean-api/src/main/java/io/ebean/event/ShutdownManager.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; /** * Manages the shutdown of the JVM Runtime. @@ -22,6 +23,8 @@ public final class ShutdownManager { private static final Logger logger = LoggerFactory.getLogger(ShutdownManager.class); + private static final ReentrantLock lock = new ReentrantLock(false); + private static final List databases = new ArrayList<>(); private static final ShutdownHook shutdownHook = new ShutdownHook(); @@ -56,9 +59,11 @@ public final class ShutdownManager { * Return true if the system is in the process of stopping. */ public static boolean isStopping() { - //noinspection SynchronizationOnStaticField - synchronized (databases) { + lock.lock(); + try { return stopping; + } finally { + lock.unlock(); } } @@ -74,15 +79,15 @@ public final class ShutdownManager { *

*/ public static void deregisterShutdownHook() { - //noinspection SynchronizationOnStaticField - synchronized (databases) { - try { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } + lock.lock(); + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; } + } finally { + lock.unlock(); } } @@ -90,18 +95,18 @@ public final class ShutdownManager { * Register the shutdown hook with the Runtime. */ protected static void registerShutdownHook() { - //noinspection SynchronizationOnStaticField - synchronized (databases) { - try { - String value = System.getProperty("ebean.registerShutdownHook"); - if (value == null || !value.trim().equalsIgnoreCase("false")) { - Runtime.getRuntime().addShutdownHook(shutdownHook); - } - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } + lock.lock(); + try { + String value = System.getProperty("ebean.registerShutdownHook"); + if (value == null || !value.trim().equalsIgnoreCase("false")) { + Runtime.getRuntime().addShutdownHook(shutdownHook); } + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; + } + } finally { + lock.unlock(); } } @@ -112,8 +117,8 @@ public final class ShutdownManager { *

*/ public static void shutdown() { - //noinspection SynchronizationOnStaticField - synchronized (databases) { + lock.lock(); + try { if (stopping) { // Already run shutdown... return; @@ -157,6 +162,8 @@ public final class ShutdownManager { if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) { deregisterAllJdbcDrivers(); } + } finally { + lock.unlock(); } } @@ -178,9 +185,11 @@ public final class ShutdownManager { * Register an ebeanServer to be shutdown when the JVM is shutdown. */ public static void registerDatabase(Database server) { - //noinspection SynchronizationOnStaticField - synchronized (databases) { + lock.lock(); + try { databases.add(server); + } finally { + lock.unlock(); } } @@ -191,9 +200,11 @@ public final class ShutdownManager { *

*/ public static void unregisterDatabase(Database server) { - //noinspection SynchronizationOnStaticField - synchronized (databases) { + lock.lock(); + try { databases.remove(server); + } finally { + lock.unlock(); } } diff --git a/ebean-api/src/main/java/io/ebean/plugin/SpiServer.java b/ebean-api/src/main/java/io/ebean/plugin/SpiServer.java index b5c833e12..b2028cadc 100644 --- a/ebean-api/src/main/java/io/ebean/plugin/SpiServer.java +++ b/ebean-api/src/main/java/io/ebean/plugin/SpiServer.java @@ -12,7 +12,7 @@ import java.util.List; /** * Extensions to Database API made available to plugins. */ -public interface SpiServer extends EbeanServer, BeanLoader { +public interface SpiServer extends EbeanServer { /** * Return the DatabaseConfig. @@ -54,6 +54,11 @@ public interface SpiServer extends EbeanServer, BeanLoader { */ DataSource getReadOnlyDataSource(); + /** + * Return a BeanLoader. + */ + BeanLoader beanLoader(); + /** * Invoke lazy loading on this single bean (reference bean). */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java index 968c5f788..d2d38e7cf 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.locks.ReentrantLock; /** * Manages the construction of caches. @@ -26,17 +27,14 @@ class DefaultCacheHolder { private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.ALL"); + private final ReentrantLock lock = new ReentrantLock(false); private final ConcurrentHashMap allCaches = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> collectIdCaches = new ConcurrentHashMap<>(); private final ServerCacheFactory cacheFactory; - private final ServerCacheOptions beanDefault; private final ServerCacheOptions queryDefault; - private final CurrentTenantProvider tenantProvider; - private final QueryCacheEntryValidate queryCacheEntryValidate; DefaultCacheHolder(CacheManagerOptions builder) { @@ -87,8 +85,11 @@ class DefaultCacheHolder { private ServerCache createCache(Class beanType, ServerCacheType type, String key, String shortName) { ServerCacheOptions options = getCacheOptions(beanType, type); if (type == ServerCacheType.COLLECTION_IDS) { - synchronized (this) { + lock.lock(); + try { collectIdCaches.computeIfAbsent(beanType.getName(), s -> new ConcurrentSkipListSet<>()).add(key); + } finally { + lock.unlock(); } } return cacheFactory.createCache(new ServerCacheConfig(type, key, shortName, options, tenantProvider, queryCacheEntryValidate)); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java index 514331b7c..face12592 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/cluster/ClusterManager.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** * Manages the cluster service. @@ -17,6 +18,8 @@ public class ClusterManager implements ServerLookup { private static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster"); + private final ReentrantLock lock = new ReentrantLock(false); + private final ConcurrentHashMap serverMap = new ConcurrentHashMap<>(); private final Object monitor = new Object(); @@ -51,18 +54,24 @@ public class ClusterManager implements ServerLookup { } public void registerServer(EbeanServer server) { - synchronized (monitor) { + lock.lock(); + try { serverMap.put(server.getName(), server); if (!started) { startup(); } + } finally { + lock.unlock(); } } @Override public EbeanServer getServer(String name) { - synchronized (monitor) { + lock.lock(); + try { return serverMap.get(name); + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java index 777af3605..46f9124c0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java @@ -26,6 +26,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.ServiceLoader; +import java.util.concurrent.locks.ReentrantLock; /** * Default Server side implementation of ServerFactory. @@ -34,6 +35,7 @@ public class DefaultContainer implements SpiContainer { private static final Logger logger = LoggerFactory.getLogger("io.ebean.internal.DefaultContainer"); + private final ReentrantLock lock = new ReentrantLock(false); private final ClusterManager clusterManager; public DefaultContainer(ContainerConfig containerConfig) { @@ -72,7 +74,8 @@ public class DefaultContainer implements SpiContainer { */ @Override public SpiEbeanServer createServer(DatabaseConfig config) { - synchronized (this) { + lock.lock(); + try { applyConfigServices(config); setNamingConvention(config); BootupClasses bootupClasses = getBootupClasses(config); @@ -114,6 +117,8 @@ public class DefaultContainer implements SpiContainer { } DbOffline.reset(); return server; + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 6387ce24b..f683538ec 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -36,11 +36,13 @@ import io.ebean.Version; import io.ebean.annotation.Platform; import io.ebean.annotation.TxIsolation; import io.ebean.bean.BeanCollection; +import io.ebean.bean.BeanLoader; import io.ebean.bean.CallOrigin; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; import io.ebean.bean.PersistenceContext.WithOption; +import io.ebean.bean.SingleBeanLoader; import io.ebean.cache.ServerCacheManager; import io.ebean.common.CopyOnFirstWriteList; import io.ebean.config.CurrentTenantProvider; @@ -142,6 +144,7 @@ import java.util.Optional; import java.util.Set; import java.util.Spliterator; import java.util.concurrent.Callable; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -157,98 +160,47 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class); + private final ReentrantLock lock = new ReentrantLock(false); private final DatabaseConfig config; - private final String serverName; - private final DatabasePlatform databasePlatform; - private final TransactionManager transactionManager; - private final QueryPlanManager queryPlanManager; - private final ExtraMetrics extraMetrics; - private final DataTimeZone dataTimeZone; - - /** - * Clock to use for WhenModified and WhenCreated. - */ private final ClockService clockService; - private final CallOriginFactory callStackFactory; - - /** - * Handles the save, delete, updateSql CallableSql. - */ private final Persister persister; - private final OrmQueryEngine queryEngine; - private final RelationalQueryEngine relationalQueryEngine; private final DtoQueryEngine dtoQueryEngine; - private final ServerCacheManager serverCacheManager; - private final DtoBeanManager dtoBeanManager; private final BeanDescriptorManager beanDescriptorManager; - private final AutoTuneService autoTuneService; - private final ReadAuditPrepare readAuditPrepare; - private final ReadAuditLogger readAuditLogger; - private final CQueryEngine cqueryEngine; - private final List serverPlugins; - private final SpiDdlGenerator ddlGenerator; - private final ScriptRunner scriptRunner; - private final ExpressionFactory expressionFactory; - private final SpiBackgroundExecutor backgroundExecutor; - private final DefaultBeanLoader beanLoader; - private final EncryptKeyManager encryptKeyManager; - private final SpiJsonContext jsonContext; - private final DocumentStore documentStore; - private final MetaInfoManager metaInfoManager; - private final CurrentTenantProvider currentTenantProvider; - private final SpiLogManager logManager; - - /** - * The default PersistenceContextScope used if it is not explicitly set on a query. - */ private final PersistenceContextScope defaultPersistenceContextScope; - - /** - * Flag set when the server has shutdown. - */ - private boolean shutdown; - - /** - * The default batch size for lazy loading beans or collections. - */ private final int lazyLoadBatchSize; - private final int queryBatchSize; - private final boolean updateAllPropertiesInBatch; - private final long slowQueryMicros; - private final SlowQueryListener slowQueryListener; - private final boolean disableL2Cache; + private boolean shutdown; /** * Create the DefaultServer. @@ -452,8 +404,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public void shutdown() { - synchronized (this) { + lock.lock(); + try { shutdownInternal(true, false); + } finally { + lock.unlock(); } } @@ -462,10 +417,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { */ @Override public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) { - synchronized (this) { - // Unregister from JVM Shutdown hook + lock.lock(); + try { ShutdownManager.unregisterDatabase(this); shutdownInternal(shutdownDataSource, deregisterDriver); + } finally { + lock.unlock(); } } @@ -575,6 +532,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { beanLoader.loadBean(loadRequest); } + @Override + public BeanLoader beanLoader() { + return new SingleBeanLoader.Dflt(this); + } + @Override public void loadBean(EntityBeanIntercept ebi) { beanLoader.loadBean(ebi); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternString.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternString.java index 0e1feacc8..c952f8f2d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternString.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternString.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.core; import java.util.HashMap; +import java.util.concurrent.locks.ReentrantLock; /** * Used to reduce memory consumption of strings used in deployment processing. @@ -12,6 +13,8 @@ public final class InternString { private static final HashMap map = new HashMap<>(); + private static final ReentrantLock lock = new ReentrantLock(false); + /** * Return the shared instance of this string. */ @@ -19,8 +22,8 @@ public final class InternString { if (s == null) { return null; } - //noinspection SynchronizationOnStaticField - synchronized (map) { + lock.lock(); + try { String v = map.get(s); if (v != null) { return v; @@ -28,6 +31,8 @@ public final class InternString { map.put(s, s); return s; } + } finally { + lock.unlock(); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/idgen/UuidV1RndIdGenerator.java b/ebean-core/src/main/java/io/ebeaninternal/server/idgen/UuidV1RndIdGenerator.java index 4db84223d..d9928c422 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/idgen/UuidV1RndIdGenerator.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/idgen/UuidV1RndIdGenerator.java @@ -7,6 +7,7 @@ import java.security.SecureRandom; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +45,8 @@ public class UuidV1RndIdGenerator implements PlatformIdGenerator { private AtomicLong nanoToMilliOffset = new AtomicLong(currentUuidTime()); + private final ReentrantLock lock = new ReentrantLock(false); + /** * Returns the uuid epoch. @@ -106,7 +109,8 @@ public class UuidV1RndIdGenerator implements PlatformIdGenerator { logger.info("Clock skew of {} ms detected", delta / -10000); // The clock was adjusted back about 2 seconds, or we were generating a lot of ids too fast // if so, we try to set the current as last and also increment the clockSeq. - synchronized (this) { + lock.lock(); + try { if (clockSeq.compareAndSet(seq, seq + 1)) { timeStamp.set(current); saveState(); @@ -114,6 +118,8 @@ public class UuidV1RndIdGenerator implements PlatformIdGenerator { } else { continue; } + } finally { + lock.unlock(); } } // If current is in the future (most of the cases) try to set it. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonExecutorService.java b/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonExecutorService.java index c2c0a4370..48883f87f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonExecutorService.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonExecutorService.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** * A "CachedThreadPool" based on Daemon threads. @@ -16,6 +17,8 @@ public final class DaemonExecutorService { private static final Logger logger = LoggerFactory.getLogger(DaemonExecutorService.class); + private final ReentrantLock lock = new ReentrantLock(false); + private final String namePrefix; private final int shutdownWaitSeconds; @@ -49,7 +52,8 @@ public final class DaemonExecutorService { *

*/ public void shutdown() { - synchronized (this) { + lock.lock(); + try { if (service.isShutdown()) { logger.debug("DaemonExecutorService[{}] already shut down", namePrefix); return; @@ -66,6 +70,8 @@ public final class DaemonExecutorService { logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e); e.printStackTrace(); } + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonScheduleThreadPool.java b/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonScheduleThreadPool.java index e3b4e0be0..f3ee018af 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonScheduleThreadPool.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/lib/DaemonScheduleThreadPool.java @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** * Daemon based ScheduleThreadPool. @@ -16,6 +17,8 @@ public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor private static final Logger logger = LoggerFactory.getLogger(DaemonScheduleThreadPool.class); + private final ReentrantLock lock = new ReentrantLock(false); + private final String namePrefix; private final int shutdownWaitSeconds; @@ -38,7 +41,8 @@ public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor */ @Override public void shutdown() { - synchronized (this) { + lock.lock(); + try { if (super.isShutdown()) { logger.debug("DaemonScheduleThreadPool {} already shut down", namePrefix); return; @@ -55,6 +59,8 @@ public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor logger.error("Error during shutdown of " + namePrefix, e); e.printStackTrace(); } + } finally { + lock.unlock(); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBaseContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBaseContext.java index 269890081..fc2213db5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBaseContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBaseContext.java @@ -7,11 +7,15 @@ import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.querydefn.OrmQueryProperties; +import java.util.concurrent.locks.ReentrantLock; + /** * Base class for Bean and BeanCollection loading (lazy loading and query join loading). */ abstract class DLoadBaseContext { + protected final ReentrantLock lock = new ReentrantLock(false); + protected final DLoadContext parent; protected final BeanDescriptor desc; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java index e2c103bbb..f33d597e8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -17,6 +17,8 @@ import io.ebeaninternal.server.querydefn.OrmQueryProperties; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * ToOne bean load context. @@ -86,11 +88,11 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { @Override public void loadSecondaryQuery(OrmQueryRequest parentRequest, boolean forEach) { - if (!queryFetch) { throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?"); } - synchronized (this) { + lock.lock(); + try { if (bufferList != null) { for (LoadBuffer loadBuffer : bufferList) { if (!loadBuffer.list.isEmpty()) { @@ -108,6 +110,8 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { } } } + } finally { + lock.unlock(); } } @@ -116,6 +120,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { */ static class LoadBuffer implements BeanLoader, LoadBeanBuffer { + private final ReentrantLock bufferLock = new ReentrantLock(false); private final DLoadBeanContext context; private final int batchSize; private final List list; @@ -127,6 +132,12 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { this.list = new ArrayList<>(batchSize); } + @Override + public Lock lock() { + bufferLock.lock(); + return bufferLock; + } + @Override public int getBatchSize() { return batchSize; @@ -182,7 +193,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext { @Override public void loadBean(EntityBeanIntercept ebi) { - // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean() + // A lock is effectively held by EntityBeanIntercept.loadBean() if (context.desc.lazyLoadMany(ebi, context)) { // lazy load property was a Many return; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java index d58bcdcf0..1f1685485 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadManyContext.java @@ -16,6 +16,7 @@ import io.ebeaninternal.server.querydefn.OrmQueryProperties; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; /** * ToMany bean load context. @@ -94,11 +95,11 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { @Override public void loadSecondaryQuery(OrmQueryRequest parentRequest, boolean forEach) { - if (!queryFetch) { throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?"); } - synchronized (this) { + lock.lock(); + try { if (bufferList != null) { for (LoadBuffer loadBuffer : bufferList) { if (!loadBuffer.list.isEmpty()) { @@ -118,6 +119,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { this.bufferList = null; } } + } finally { + lock.unlock(); } } @@ -127,6 +130,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { */ static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer { + private final ReentrantLock lock = new ReentrantLock(false); private final PersistenceContext persistenceContext; private final DLoadManyContext context; private final int batchSize; @@ -208,7 +212,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { @Override public void loadMany(BeanCollection bc, boolean onlyIds) { - synchronized (this) { + lock.lock(); + try { boolean useCache = !onlyIds && context.hitCache && context.property.isUseCache(); if (useCache) { EntityBean ownerBean = bc.getOwnerBean(); @@ -231,6 +236,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext { LoadManyRequest req = new LoadManyRequest(this, onlyIds, useCache); context.parent.getEbeanServer().loadMany(req); + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java index 379ca4396..3aeac57f5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; /** * An object that represents a SqlSelect statement. @@ -58,6 +59,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran private static final CQueryCollectionAddNoop NOOP_ADD = new CQueryCollectionAddNoop(); + private final ReentrantLock lock = new ReentrantLock(false); + /** * The resultSet rows read. */ @@ -288,7 +291,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran @Override public void cancel() { - synchronized (this) { + lock.lock(); + try { this.cancelled = true; if (pstmt != null) { try { @@ -298,6 +302,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran throw new PersistenceException(msg, e); } } + } finally { + lock.unlock(); } } @@ -326,8 +332,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran } ResultSet prepareResultSet(boolean forwardOnlyHint) throws SQLException { - - synchronized (this) { + lock.lock(); + try { if (cancelled || query.isCancelled()) { // cancelled before we started cancelled = true; @@ -370,6 +376,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran // executeQuery return pstmt.executeQuery(); + } finally { + lock.unlock(); } } @@ -524,8 +532,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran } boolean hasNext() throws SQLException { - - synchronized (this) { + lock.lock(); + try { if (noMoreRows || cancelled) { return false; } @@ -534,6 +542,8 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran } hasNextCache = readNextBean(); return hasNextCache; + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java index 30e319ad8..c4e7ab5bc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBindCapture.java @@ -5,10 +5,13 @@ import io.ebeaninternal.api.SpiQueryBindCapture; import io.ebeaninternal.api.SpiQueryPlan; import io.ebeaninternal.server.type.bindcapture.BindCapture; +import java.util.concurrent.locks.ReentrantLock; + class CQueryBindCapture implements SpiQueryBindCapture { private static final double multiplier = 1.3d; + private final ReentrantLock lock = new ReentrantLock(false); private final CQueryPlanManager manager; private final SpiQueryPlan queryPlan; @@ -35,13 +38,16 @@ class CQueryBindCapture implements SpiQueryBindCapture { @Override public void setBind(BindCapture bindCapture, long queryTimeMicros, long startNanos) { - synchronized (this) { + lock.lock(); + try { this.thresholdMicros = Math.round(queryTimeMicros * multiplier); this.captureCount++; this.bindCapture = bindCapture; this.queryTimeMicros = queryTimeMicros; lastBindCapture = System.currentTimeMillis(); manager.notifyBindCapture(this, startNanos); + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/LimitOffsetPagedList.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/LimitOffsetPagedList.java index b4496a56e..6aa3ac38b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/LimitOffsetPagedList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/LimitOffsetPagedList.java @@ -1,31 +1,29 @@ package io.ebeaninternal.server.query; import io.ebean.PagedList; -import io.ebeaninternal.api.Monitor; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiQuery; import javax.persistence.PersistenceException; import java.util.List; import java.util.concurrent.Future; +import java.util.concurrent.locks.ReentrantLock; /** * PagedList implementation based on limit offset types of queries. - * - * @param the entity bean type */ public class LimitOffsetPagedList implements PagedList { private final transient SpiEbeanServer server; + private final transient ReentrantLock lock = new ReentrantLock(false); + private final SpiQuery query; private final int firstRow; private final int maxRows; - private final Monitor monitor = new Monitor(); - private int foregroundTotalRowCount = -1; private Future futureRowCount; @@ -49,21 +47,27 @@ public class LimitOffsetPagedList implements PagedList { @Override public Future getFutureCount() { - synchronized (monitor) { + lock.lock(); + try { if (futureRowCount == null) { futureRowCount = server.findFutureCount(query, null); } return futureRowCount; + } finally { + lock.unlock(); } } @Override public List getList() { - synchronized (monitor) { + lock.lock(); + try { if (list == null) { list = server.findList(query, null); } return list; + } finally { + lock.unlock(); } } @@ -77,7 +81,6 @@ public class LimitOffsetPagedList implements PagedList { @Override public int getTotalPageCount() { - int rowCount = getTotalCount(); if (rowCount == 0) { return 0; @@ -88,7 +91,8 @@ public class LimitOffsetPagedList implements PagedList { @Override public int getTotalCount() { - synchronized (monitor) { + lock.lock(); + try { if (futureRowCount != null) { try { // background query already initiated so get it with a wait @@ -103,6 +107,8 @@ public class LimitOffsetPagedList implements PagedList { // just using foreground thread foregroundTotalRowCount = server.findCount(query, null); return foregroundTotalRowCount; + } finally { + lock.unlock(); } } @@ -123,11 +129,9 @@ public class LimitOffsetPagedList implements PagedList { @Override public String getDisplayXtoYofZ(String to, String of) { - int first = firstRow + 1; int last = firstRow + getList().size(); int total = getTotalCount(); - return first + to + last + of + total; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index bc82a9841..21fb58d63 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -71,6 +71,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -88,6 +89,8 @@ public class DefaultOrmQuery implements SpiQuery { private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy(); + private final ReentrantLock lock = new ReentrantLock(false); + private final Class beanType; private final ExpressionFactory expressionFactory; @@ -1992,8 +1995,11 @@ public class DefaultOrmQuery implements SpiQuery { @Override public void setCancelableQuery(CancelableQuery cancelableQuery) { - synchronized (this) { + lock.lock(); + try { this.cancelableQuery = cancelableQuery; + } finally { + lock.unlock(); } } @@ -2026,18 +2032,24 @@ public class DefaultOrmQuery implements SpiQuery { @Override public void cancel() { - synchronized (this) { + lock.lock(); + try { cancelled = true; if (cancelableQuery != null) { cancelableQuery.cancel(); } + } finally { + lock.unlock(); } } @Override public boolean isCancelled() { - synchronized (this) { + lock.lock(); + try { return cancelled; + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultPersistenceContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultPersistenceContext.java index bdbb22ea0..85733cd5b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultPersistenceContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultPersistenceContext.java @@ -1,12 +1,12 @@ package io.ebeaninternal.server.transaction; import io.ebean.bean.PersistenceContext; -import io.ebeaninternal.api.Monitor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; /** * Default implementation of PersistenceContext. @@ -32,7 +32,7 @@ public final class DefaultPersistenceContext implements PersistenceContext { */ private final HashMap, ClassContext> typeCache = new HashMap<>(); - private final Monitor monitor = new Monitor(); + private final ReentrantLock lock = new ReentrantLock(false); private int putCount; @@ -68,7 +68,8 @@ public final class DefaultPersistenceContext implements PersistenceContext { } public boolean resetLimit() { - synchronized (monitor) { + lock.lock(); + try { if (putCount < 100) { return false; } @@ -80,6 +81,8 @@ public final class DefaultPersistenceContext implements PersistenceContext { } // checking after another 100 puts return false; + } finally { + lock.unlock(); } } @@ -88,17 +91,23 @@ public final class DefaultPersistenceContext implements PersistenceContext { */ @Override public void put(Class rootType, Object id, Object bean) { - synchronized (monitor) { + lock.lock(); + try { putCount++; getClassContext(rootType).put(id, bean); + } finally { + lock.unlock(); } } @Override public Object putIfAbsent(Class rootType, Object id, Object bean) { - synchronized (monitor) { + lock.lock(); + try { putCount++; return getClassContext(rootType).putIfAbsent(id, bean); + } finally { + lock.unlock(); } } @@ -107,15 +116,21 @@ public final class DefaultPersistenceContext implements PersistenceContext { */ @Override public Object get(Class rootType, Object id) { - synchronized (monitor) { + lock.lock(); + try { return getClassContext(rootType).get(id); + } finally { + lock.unlock(); } } @Override public WithOption getWithOption(Class rootType, Object id) { - synchronized (monitor) { + lock.lock(); + try { return getClassContext(rootType).getWithOption(id); + } finally { + lock.unlock(); } } @@ -124,9 +139,12 @@ public final class DefaultPersistenceContext implements PersistenceContext { */ @Override public int size(Class rootType) { - synchronized (monitor) { + lock.lock(); + try { ClassContext classMap = typeCache.get(rootType); return classMap == null ? 0 : classMap.size(); + } finally { + lock.unlock(); } } @@ -135,45 +153,60 @@ public final class DefaultPersistenceContext implements PersistenceContext { */ @Override public void clear() { - synchronized (monitor) { + lock.lock(); + try { typeCache.clear(); + } finally { + lock.unlock(); } } @Override public void clear(Class rootType) { - synchronized (monitor) { + lock.lock(); + try { ClassContext classMap = typeCache.get(rootType); if (classMap != null) { classMap.clear(); } + } finally { + lock.unlock(); } } @Override public void deleted(Class rootType, Object id) { - synchronized (monitor) { + lock.lock(); + try { ClassContext classMap = typeCache.get(rootType); if (classMap != null && id != null) { classMap.deleted(id); } + } finally { + lock.unlock(); } } @Override public void clear(Class rootType, Object id) { - synchronized (monitor) { + lock.lock(); + try { ClassContext classMap = typeCache.get(rootType); if (classMap != null && id != null) { classMap.remove(id); } + } finally { + lock.unlock(); } } @Override public String toString() { - synchronized (monitor) { + lock.lock(); + try { return typeCache.toString(); + } finally { + lock.unlock(); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java index 12cfd436f..6d6dbb42e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java @@ -21,6 +21,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.HOUR_OF_DAY; @@ -65,6 +66,8 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin { private final ExecutorService executor; + private final ReentrantLock lock = new ReentrantLock(false); + private final File dir; private final long minMicros; @@ -117,7 +120,8 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin { } private void flushCurrentFile() { - synchronized (this) { + lock.lock(); + try { if (out != null) { try { out.close(); @@ -126,6 +130,8 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin { log.error("Failed to flush and close transaction profiling file ", e); } } + } finally { + lock.unlock(); } } @@ -133,7 +139,8 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin { * Move to the next file to write to. */ private void incrementFile() { - synchronized (this) { + lock.lock(); + try { flushCurrentFile(); try { String now = DTF.format(LocalDateTime.now()); @@ -142,6 +149,8 @@ public class DefaultProfileHandler implements SpiProfileHandler, Plugin { } catch (IOException e) { log.error("Not expected", e); } + } finally { + lock.unlock(); } }