You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/02/15 22:15:36 UTC

[commons-pool] 04/06: Add and use java.time.Duration APIs timeouts instead of using ints for seconds.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git

commit 11af26e207f6f759ee368306499f456992c34adf
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Feb 15 16:56:19 2021 -0500

    Add and use java.time.Duration APIs timeouts instead of using ints for
    seconds.
---
 src/changes/changes.xml                            |    8 +-
 .../apache/commons/pool2/impl/AbandonedConfig.java |    2 +-
 .../commons/pool2/impl/BaseGenericObjectPool.java  |  202 ++-
 .../commons/pool2/impl/BaseObjectPoolConfig.java   |  169 +-
 .../commons/pool2/impl/LinkedBlockingDeque.java    | 1677 +++++++++++---------
 .../apache/commons/pool2/impl/PoolImplUtils.java   |   47 +-
 .../apache/commons/pool2/ObjectPoolIssue326.java   |    7 +-
 .../java/org/apache/commons/pool2/PoolTest.java    |    6 +
 .../pool2/impl/TestAbandonedObjectPool.java        |   19 +-
 .../pool2/impl/TestBaseGenericObjectPool.java      |    9 +-
 .../apache/commons/pool2/impl/TestConstants.java   |    1 +
 .../pool2/impl/TestGenericKeyedObjectPool.java     |   94 +-
 .../commons/pool2/impl/TestGenericObjectPool.java  |  123 +-
 .../impl/TestGenericObjectPoolClassLoaders.java    |    5 +-
 .../TestGenericObjectPoolFactoryCreateFailure.java |    7 +-
 .../pool2/impl/TestLinkedBlockingDeque.java        |   30 +-
 16 files changed, 1444 insertions(+), 962 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ab72916..d81cb69 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -45,11 +45,9 @@ The <action> type attribute can be add,update,fix,remove.
   <body>
   <release version="2.10.0" date="202Y-MM-DD" description="This is a MMMM release (Java 8).">
     <!-- ADD -->
-    <action dev="ggregory" type="update" due-to="Dependabot">
-      Add and use java.time.Duration APIs for AbandonedConfig timeouts instead of using ints for seconds.
-      - Add AbandonedConfig.getRemoveAbandonedTimeoutDuration().
-      - Add AbandonedConfig.setRemoveAbandonedTimeout(Duration).
-      - Add GenericObjectPool.getRemoveAbandonedTimeoutDuration().
+    <action dev="ggregory" type="add" due-to="Dependabot">
+      Add and use java.time.Duration APIs timeouts instead of using ints for seconds.
+      See the site and its API comparison report for a list of the new Duration-based APIs.
     </action>
     <!-- UPDATE -->
     <action dev="ggregory" type="update" due-to="Dependabot">
diff --git a/src/main/java/org/apache/commons/pool2/impl/AbandonedConfig.java b/src/main/java/org/apache/commons/pool2/impl/AbandonedConfig.java
index 0f0b94e..0d94c7d 100644
--- a/src/main/java/org/apache/commons/pool2/impl/AbandonedConfig.java
+++ b/src/main/java/org/apache/commons/pool2/impl/AbandonedConfig.java
@@ -131,7 +131,7 @@ public class AbandonedConfig {
      * <p>If set to true, abandoned objects are removed by the pool
      * maintenance thread when it runs.  This setting has no effect
      * unless maintenance is enabled by setting
-     *{@link GenericObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis}
+     *{@link GenericObjectPool#getTimeBetweenEvictionRuns() timeBetweenEvictionRuns}
      * to a positive number.</p>
      *
      * @return true if abandoned objects are to be removed by the evictor
diff --git a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
index f4bcb2c..a642426 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
@@ -146,7 +146,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
 
 
     /**
-     * Returns the maximum number of objects that can be allocated by the pool
+     * Gets the maximum number of objects that can be allocated by the pool
      * (checked out to clients, or idle awaiting checkout) at a given time. When
      * negative, there is no limit to the number of objects that can be
      * managed by the pool at one time.
@@ -218,9 +218,9 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
         setTestOnReturn(config.getTestOnReturn());
         setTestWhileIdle(config.getTestWhileIdle());
         setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun());
-        setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis());
-        setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis());
-        setSoftMinEvictableIdleTimeMillis(config.getSoftMinEvictableIdleTimeMillis());
+        setMinEvictableIdleTime(config.getMinEvictableIdleTime());
+        setTimeBetweenEvictionRuns(config.getTimeBetweenEvictionRuns());
+        setSoftMinEvictableIdleTime(config.getSoftMinEvictableIdleTime());
         final EvictionPolicy<T> policy = config.getEvictionPolicy();
         if (policy == null) {
             // Use the class name (pre-2.6.0 compatible)
@@ -229,11 +229,11 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
             // Otherwise, use the class (2.6.0 feature)
             setEvictionPolicy(policy);
         }
-        setEvictorShutdownTimeoutMillis(config.getEvictorShutdownTimeoutMillis());
+        setEvictorShutdownTimeout(config.getEvictorShutdownTimeout());
     }
 
     /**
-     * Returns the maximum amount of time (in milliseconds) the
+     * Gets the maximum amount of time (in milliseconds) the
      * {@code borrowObject()} method should block before throwing an
      * exception when the pool is exhausted and
      * {@link #getBlockWhenExhausted} is true. When less than 0, the
@@ -418,7 +418,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     /**
      * Returns whether objects sitting idle in the pool will be validated by the
      * idle object evictor (if any - see
-     * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed
+     * {@link #setTimeBetweenEvictionRuns(Duration)}). Validation is performed
      * by the {@code validateObject()} method of the factory associated
      * with the pool. If the object fails to validate, it will be removed from
      * the pool and destroyed.
@@ -435,7 +435,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     /**
      * Returns whether objects sitting idle in the pool will be validated by the
      * idle object evictor (if any - see
-     * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed
+     * {@link #setTimeBetweenEvictionRuns(Duration)}). Validation is performed
      * by the {@code validateObject()} method of the factory associated
      * with the pool. If the object fails to validate, it will be removed from
      * the pool and destroyed.  Note that setting this property has no effect
@@ -446,21 +446,37 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *            {@code true} so objects will be validated by the evictor
      *
      * @see #getTestWhileIdle
-     * @see #setTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRuns
      */
     public final void setTestWhileIdle(final boolean testWhileIdle) {
         this.testWhileIdle = testWhileIdle;
     }
 
     /**
-     * Returns the number of milliseconds to sleep between runs of the idle
+     * Gets the duration to sleep between runs of the idle
+     * object evictor thread. When non-positive, no idle object evictor thread
+     * will be run.
+     *
+     * @return number of milliseconds to sleep between evictor runs
+     *
+     * @see #setTimeBetweenEvictionRuns
+     * @since 2.10.0
+     */
+    public final Duration getTimeBetweenEvictionRuns() {
+        return timeBetweenEvictionRuns;
+    }
+
+    /**
+     * Gets the number of milliseconds to sleep between runs of the idle
      * object evictor thread. When non-positive, no idle object evictor thread
      * will be run.
      *
      * @return number of milliseconds to sleep between evictor runs
      *
      * @see #setTimeBetweenEvictionRunsMillis
+     * @deprecated Use {@link #getTimeBetweenEvictionRuns()}.
      */
+    @Deprecated
     public final long getTimeBetweenEvictionRunsMillis() {
         return timeBetweenEvictionRuns.toMillis();
     }
@@ -472,19 +488,38 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      * <li>When non-positive, no idle object evictor thread runs.</li>
      * </ul>
      *
+     * @param timeBetweenEvictionRuns
+     *            duration to sleep between evictor runs
+     *
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @since 2.10.0
+     */
+    public final void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) {
+        this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
+        startEvictor(this.timeBetweenEvictionRuns);
+    }
+
+    /**
+     * Sets the number of milliseconds to sleep between runs of the idle object evictor thread.
+     * <ul>
+     * <li>When positive, the idle object evictor thread starts.</li>
+     * <li>When non-positive, no idle object evictor thread runs.</li>
+     * </ul>
+     *
      * @param timeBetweenEvictionRunsMillis
      *            number of milliseconds to sleep between evictor runs
      *
      * @see #getTimeBetweenEvictionRunsMillis
+     * @deprecated Use {@link #setTimeBetweenEvictionRuns(Duration)}.
      */
-    public final void setTimeBetweenEvictionRunsMillis(
-            final long timeBetweenEvictionRunsMillis) {
+    @Deprecated
+    public final void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
         this.timeBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
         startEvictor(timeBetweenEvictionRuns);
     }
 
     /**
-     * Returns the maximum number of objects to examine during each run (if any)
+     * Gets the maximum number of objects to examine during each run (if any)
      * of the idle object evictor thread. When positive, the number of tests
      * performed for a run will be the minimum of the configured value and the
      * number of idle instances in the pool. When negative, the number of tests
@@ -523,7 +558,24 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     }
 
     /**
-     * Returns the minimum amount of time an object may sit idle in the pool
+     * Gets the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor (if any -
+     * see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive,
+     * no objects will be evicted from the pool due to idle time alone.
+     *
+     * @return minimum amount of time an object may sit idle in the pool before
+     *         it is eligible for eviction
+     *
+     * @see #setMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     * @since 2.10.0
+     */
+    public final Duration getMinEvictableIdleTime() {
+        return minEvictableIdleTime;
+    }
+
+    /**
+     * Gets the minimum amount of time an object may sit idle in the pool
      * before it is eligible for eviction by the idle object evictor (if any -
      * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive,
      * no objects will be evicted from the pool due to idle time alone.
@@ -533,7 +585,9 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *
      * @see #setMinEvictableIdleTimeMillis
      * @see #setTimeBetweenEvictionRunsMillis
+     * @deprecated Use {@link #getMinEvictableIdleTime()}.
      */
+    @Deprecated
     public final long getMinEvictableIdleTimeMillis() {
         return minEvictableIdleTime.toMillis();
     }
@@ -541,6 +595,24 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     /**
      * Sets the minimum amount of time an object may sit idle in the pool
      * before it is eligible for eviction by the idle object evictor (if any -
+     * see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive,
+     * no objects will be evicted from the pool due to idle time alone.
+     *
+     * @param minEvictableIdleTimeMillis
+     *            minimum amount of time an object may sit idle in the pool
+     *            before it is eligible for eviction
+     *
+     * @see #getMinEvictableIdleTime
+     * @see #setTimeBetweenEvictionRuns
+     * @since 2.10.0
+     */
+    public final void setMinEvictableIdleTime(final Duration minEvictableIdleTimeMillis) {
+        this.minEvictableIdleTime = minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * Sets the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor (if any -
      * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive,
      * no objects will be evicted from the pool due to idle time alone.
      *
@@ -550,14 +622,35 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *
      * @see #getMinEvictableIdleTimeMillis
      * @see #setTimeBetweenEvictionRunsMillis
+     * @deprecated Use {@link #setMinEvictableIdleTime(Duration)}.
      */
-    public final void setMinEvictableIdleTimeMillis(
-            final long minEvictableIdleTimeMillis) {
+    @Deprecated
+    public final void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
         this.minEvictableIdleTime = Duration.ofMillis(minEvictableIdleTimeMillis);
     }
 
     /**
-     * Returns the minimum amount of time an object may sit idle in the pool
+     * Gets the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor (if any -
+     * see {@link #setTimeBetweenEvictionRuns(Duration)}),
+     * with the extra condition that at least {@code minIdle} object
+     * instances remain in the pool. This setting is overridden by
+     * {@link #getMinEvictableIdleTime} (that is, if
+     * {@link #getMinEvictableIdleTime} is positive, then
+     * {@link #getSoftMinEvictableIdleTime} is ignored).
+     *
+     * @return minimum amount of time an object may sit idle in the pool before
+     *         it is eligible for eviction if minIdle instances are available
+     *
+     * @see #setSoftMinEvictableIdleTime(Duration)
+     * @since 2.10.0
+     */
+    public final Duration getSoftMinEvictableIdleTime() {
+        return softMinEvictableIdleTime;
+    }
+
+    /**
+     * Gets the minimum amount of time an object may sit idle in the pool
      * before it is eligible for eviction by the idle object evictor (if any -
      * see {@link #setTimeBetweenEvictionRunsMillis(long)}),
      * with the extra condition that at least {@code minIdle} object
@@ -570,7 +663,9 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *         it is eligible for eviction if minIdle instances are available
      *
      * @see #setSoftMinEvictableIdleTimeMillis
+     * @deprecated Use {@link #getSoftMinEvictableIdleTime()}.
      */
+    @Deprecated
     public final long getSoftMinEvictableIdleTimeMillis() {
         return softMinEvictableIdleTime.toMillis();
     }
@@ -578,6 +673,28 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     /**
      * Sets the minimum amount of time an object may sit idle in the pool
      * before it is eligible for eviction by the idle object evictor (if any -
+     * see {@link #setTimeBetweenEvictionRuns(Duration)}),
+     * with the extra condition that at least {@code minIdle} object
+     * instances remain in the pool. This setting is overridden by
+     * {@link #getMinEvictableIdleTime} (that is, if
+     * {@link #getMinEvictableIdleTime} is positive, then
+     * {@link #getSoftMinEvictableIdleTime} is ignored).
+     *
+     * @param softMinEvictableIdleTime
+     *            minimum amount of time an object may sit idle in the pool
+     *            before it is eligible for eviction if minIdle instances are
+     *            available
+     *
+     * @see #getSoftMinEvictableIdleTimeMillis
+     * @since 2.10.0
+     */
+    public final void setSoftMinEvictableIdleTime(final Duration softMinEvictableIdleTime) {
+        this.softMinEvictableIdleTime = softMinEvictableIdleTime;
+    }
+
+    /**
+     * Sets the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor (if any -
      * see {@link #setTimeBetweenEvictionRunsMillis(long)}),
      * with the extra condition that at least {@code minIdle} object
      * instances remain in the pool. This setting is overridden by
@@ -591,14 +708,15 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *            available
      *
      * @see #getSoftMinEvictableIdleTimeMillis
+     * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}.
      */
-    public final void setSoftMinEvictableIdleTimeMillis(
-            final long softMinEvictableIdleTimeMillis) {
+    @Deprecated
+    public final void setSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
         this.softMinEvictableIdleTime = Duration.ofMillis(softMinEvictableIdleTimeMillis);
     }
 
     /**
-     * Returns the name of the {@link EvictionPolicy} implementation that is
+     * Gets the name of the {@link EvictionPolicy} implementation that is
      * used by this pool.
      *
      * @return  The fully qualified class name of the {@link EvictionPolicy}
@@ -692,22 +810,48 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
      *
      * @return  The timeout in milliseconds that will be used while waiting for
      *          the Evictor to shut down.
+     * @deprecated Use {@link #getEvictorShutdownTimeout()}.
      */
+    @Deprecated
     public final long getEvictorShutdownTimeoutMillis() {
         return evictorShutdownTimeout.toMillis();
     }
 
     /**
-     * Sets the timeout that will be used when waiting for the Evictor to
+     * Gets the timeout that will be used when waiting for the Evictor to
      * shutdown if this pool is closed and it is the only pool still using the
      * the value for the Evictor.
      *
-     * @param evictorShutdownTimeoutMillis  the timeout in milliseconds that
-     *                                      will be used while waiting for the
-     *                                      Evictor to shut down.
+     * @return  The timeout that will be used while waiting for
+     *          the Evictor to shut down.
+     * @since 2.10.0
+     */
+    public final Duration getEvictorShutdownTimeout() {
+        return evictorShutdownTimeout;
+    }
+
+    /**
+     * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the
+     * only pool still using the the value for the Evictor.
+     *
+     * @param evictorShutdownTimeoutMillis the timeout in milliseconds that will be used while waiting for the Evictor
+     *                                     to shut down.
+     * @since 2.10.0
+     */
+    public final void setEvictorShutdownTimeout(final Duration evictorShutdownTimeoutMillis) {
+        this.evictorShutdownTimeout = evictorShutdownTimeoutMillis;
+    }
+
+    /**
+     * Sets the timeout that will be used when waiting for the Evictor to shutdown if this pool is closed and it is the
+     * only pool still using the the value for the Evictor.
+     *
+     * @param evictorShutdownTimeoutMillis the timeout in milliseconds that will be used while waiting for the Evictor
+     *                                     to shut down.
+     * @deprecated Use {@link #setEvictorShutdownTimeout(Duration)}.
      */
-    public final void setEvictorShutdownTimeoutMillis(
-            final long evictorShutdownTimeoutMillis) {
+    @Deprecated
+    public final void setEvictorShutdownTimeoutMillis(final long evictorShutdownTimeoutMillis) {
         this.evictorShutdownTimeout = Duration.ofMillis(evictorShutdownTimeoutMillis);
     }
 
@@ -738,7 +882,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
     public abstract void evict() throws Exception;
 
     /**
-     * Returns the {@link EvictionPolicy} defined for this pool.
+     * Gets the {@link EvictionPolicy} defined for this pool.
      *
      * @return the eviction policy
      * @since 2.4
@@ -1223,7 +1367,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
         }
 
         /**
-         * Returns the mean of the cached values.
+         * Gets the mean of the cached values.
          *
          * @return the mean of the cache, truncated to long
          */
@@ -1278,7 +1422,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
         }
 
         /**
-         * Returns the idle object deque referenced by this iterator.
+         * Gets the idle object deque referenced by this iterator.
          * @return the idle object deque
          */
         public Deque<PooledObject<T>> getIdleObjects() {
diff --git a/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java b/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
index 525aede..9147725 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
@@ -67,7 +67,9 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * configuration attribute.
      * @see GenericObjectPool#getMinEvictableIdleTimeMillis()
      * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
+     * @deprecated Use {@link #DEFAULT_MIN_EVICTABLE_IDLE_TIME}.
      */
+    @Deprecated
     public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS =
             1000L * 60L * 30L;
 
@@ -86,14 +88,16 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * configuration attribute.
      * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
      * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
+     * @deprecated Use {@link #DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME}.
      */
+    @Deprecated
     public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;
 
     /**
      * The default value for the {@code softMinEvictableIdleTime}
      * configuration attribute.
-     * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
-     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
+     * @see GenericObjectPool#getSoftMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime()
      * @since 2.10.0
      */
     public static final Duration DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME = 
@@ -104,15 +108,16 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * attribute.
      * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
      * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     * @deprecated Use {@link #DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT}.
      */
-    public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS =
-            10L * 1000L;
+    @Deprecated
+    public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS = 10L * 1000L;
 
     /**
      * The default value for {@code evictorShutdownTimeout} configuration
      * attribute.
-     * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
-     * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     * @see GenericObjectPool#getEvictorShutdownTimeout()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeout()
      * @since 2.10.0
      */
     public static final Duration DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT =
@@ -161,7 +166,9 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * configuration attribute.
      * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
      * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
+     * @deprecated Use {@link #DEFAULT_TIME_BETWEEN_EVICTION_RUNS}.
      */
+    @Deprecated
     public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L;
 
     /**
@@ -341,9 +348,26 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * @return  The current setting of {@code minEvictableIdleTime} for
      *          this configuration instance
      *
+     * @see GenericObjectPool#getMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getMinEvictableIdleTime()
+     * @since 2.10.0
+     */
+    public Duration getMinEvictableIdleTime() {
+        return minEvictableIdleTime;
+    }
+
+    /**
+     * Gets the value for the {@code minEvictableIdleTime} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code minEvictableIdleTime} for
+     *          this configuration instance
+     *
      * @see GenericObjectPool#getMinEvictableIdleTimeMillis()
      * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
+     * @deprecated Use {@link #getMinEvictableIdleTime()}.
      */
+    @Deprecated
     public long getMinEvictableIdleTimeMillis() {
         return minEvictableIdleTime.toMillis();
     }
@@ -352,12 +376,29 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * Sets the value for the {@code minEvictableIdleTime} configuration
      * attribute for pools created with this configuration instance.
      *
+     * @param minEvictableIdleTime The new setting of
+     *        {@code minEvictableIdleTime} for this configuration instance
+     *
+     * @see GenericObjectPool#getMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getMinEvictableIdleTime()
+     * @since 2.10.0
+     */
+    public void setMinEvictableIdleTime(final Duration minEvictableIdleTime) {
+        this.minEvictableIdleTime = minEvictableIdleTime;
+    }
+
+    /**
+     * Sets the value for the {@code minEvictableIdleTime} configuration
+     * attribute for pools created with this configuration instance.
+     *
      * @param minEvictableIdleTimeMillis The new setting of
      *        {@code minEvictableIdleTime} for this configuration instance
      *
      * @see GenericObjectPool#getMinEvictableIdleTimeMillis()
      * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
+     * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}.
      */
+    @Deprecated
     public void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
         this.minEvictableIdleTime = Duration.ofMillis(minEvictableIdleTimeMillis);
     }
@@ -370,9 +411,26 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * @return  The current setting of {@code softMinEvictableIdleTime}
      *          for this configuration instance
      *
-     * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
-     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
+     * @see GenericObjectPool#getSoftMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime()
+     * @since 2.10.0
+     */
+    public Duration getSoftMinEvictableIdleTime() {
+        return softMinEvictableIdleTime;
+    }
+
+    /**
+     * Gets the value for the {@code softMinEvictableIdleTime}
+     * configuration attribute for pools created with this configuration
+     * instance.
+     *
+     * @return  The current setting of {@code softMinEvictableIdleTime}
+     *          for this configuration instance
+     *
+     * @see GenericObjectPool#getSoftMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime()
      */
+    @Deprecated
     public long getSoftMinEvictableIdleTimeMillis() {
         return softMinEvictableIdleTime.toMillis();
     }
@@ -388,13 +446,32 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      *
      * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
      * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
+     * @deprecated Use {@link #setSoftMinEvictableIdleTime(Duration)}.
      */
+    @Deprecated
     public void setSoftMinEvictableIdleTimeMillis(
             final long softMinEvictableIdleTimeMillis) {
         this.softMinEvictableIdleTime = Duration.ofMillis(softMinEvictableIdleTimeMillis);
     }
 
     /**
+     * Sets the value for the {@code softMinEvictableIdleTime}
+     * configuration attribute for pools created with this configuration
+     * instance.
+     *
+     * @param softMinEvictableIdleTime The new setting of
+     *        {@code softMinEvictableIdleTime} for this configuration
+     *        instance
+     *
+     * @see GenericObjectPool#getSoftMinEvictableIdleTime()
+     * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTime()
+     * @since 2.10.0
+     */
+    public void setSoftMinEvictableIdleTime(final Duration softMinEvictableIdleTime) {
+        this.softMinEvictableIdleTime = softMinEvictableIdleTime;
+    }
+
+    /**
      * Gets the value for the {@code numTestsPerEvictionRun} configuration
      * attribute for pools created with this configuration instance.
      *
@@ -431,12 +508,45 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      *
      * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
      * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     * @deprecated Use {@link #getEvictorShutdownTimeout()}.
      */
+    @Deprecated
     public long getEvictorShutdownTimeoutMillis() {
         return evictorShutdownTimeout.toMillis();
     }
 
     /**
+     * Gets the value for the {@code evictorShutdownTimeout} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code evictorShutdownTimeout} for
+     *          this configuration instance
+     *
+     * @see GenericObjectPool#getEvictorShutdownTimeout()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeout()
+     * @since 2.10.0
+     */
+    public Duration getEvictorShutdownTimeout() {
+        return evictorShutdownTimeout;
+    }
+
+    /**
+     * Sets the value for the {@code evictorShutdownTimeout} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @param evictorShutdownTimeoutMillis The new setting of
+     *        {@code evictorShutdownTimeout} for this configuration
+     *        instance
+     *
+     * @see GenericObjectPool#getEvictorShutdownTimeout()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeout()
+     * @since 2.10.0
+     */
+    public void setEvictorShutdownTimeoutMillis(final Duration evictorShutdownTimeoutMillis) {
+        this.evictorShutdownTimeout = evictorShutdownTimeoutMillis;
+    }
+
+    /**
      * Sets the value for the {@code evictorShutdownTimeout} configuration
      * attribute for pools created with this configuration instance.
      *
@@ -446,9 +556,10 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      *
      * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
      * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     * @deprecated Use {@link #setEvictorShutdownTimeoutMillis(Duration)}.
      */
-    public void setEvictorShutdownTimeoutMillis(
-            final long evictorShutdownTimeoutMillis) {
+    @Deprecated
+    public void setEvictorShutdownTimeoutMillis(final long evictorShutdownTimeoutMillis) {
         this.evictorShutdownTimeout = Duration.ofMillis(evictorShutdownTimeoutMillis);
     }
 
@@ -575,9 +686,26 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      * @return  The current setting of {@code timeBetweenEvictionRuns} for
      *          this configuration instance
      *
+     * @see GenericObjectPool#getTimeBetweenEvictionRuns()
+     * @see GenericKeyedObjectPool#getTimeBetweenEvictionRuns()
+     * @since 2.10.0
+     */
+    public Duration getTimeBetweenEvictionRuns() {
+        return timeBetweenEvictionRuns;
+    }
+
+    /**
+     * Gets the value for the {@code timeBetweenEvictionRuns} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code timeBetweenEvictionRuns} for
+     *          this configuration instance
+     *
      * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
      * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
+     * @deprecated Use {@link #getTimeBetweenEvictionRuns()}.
      */
+    @Deprecated
     public long getTimeBetweenEvictionRunsMillis() {
         return timeBetweenEvictionRuns.toMillis();
     }
@@ -590,11 +718,28 @@ public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Clon
      *        {@code timeBetweenEvictionRuns} for this configuration
      *        instance
      *
+     * @see GenericObjectPool#getTimeBetweenEvictionRuns()
+     * @see GenericKeyedObjectPool#getTimeBetweenEvictionRuns()
+     * @since 2.10.0
+     */
+    public void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRunsMillis) {
+        this.timeBetweenEvictionRuns = timeBetweenEvictionRunsMillis;
+    }
+
+    /**
+     * Sets the value for the {@code timeBetweenEvictionRuns} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @param timeBetweenEvictionRunsMillis The new setting of
+     *        {@code timeBetweenEvictionRuns} for this configuration
+     *        instance
+     *
      * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
      * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
+     * @deprecated Use {@link #setTimeBetweenEvictionRuns(Duration)}.
      */
-    public void setTimeBetweenEvictionRunsMillis(
-            final long timeBetweenEvictionRunsMillis) {
+    @Deprecated
+    public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
         this.timeBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
     }
 
diff --git a/src/main/java/org/apache/commons/pool2/impl/LinkedBlockingDeque.java b/src/main/java/org/apache/commons/pool2/impl/LinkedBlockingDeque.java
index e024546..1a48902 100644
--- a/src/main/java/org/apache/commons/pool2/impl/LinkedBlockingDeque.java
+++ b/src/main/java/org/apache/commons/pool2/impl/LinkedBlockingDeque.java
@@ -17,6 +17,7 @@
 package org.apache.commons.pool2.impl;
 
 import java.io.Serializable;
+import java.time.Duration;
 import java.util.AbstractQueue;
 import java.util.Collection;
 import java.util.Deque;
@@ -91,7 +92,147 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * implementation, so a lot of method specs are duplicated here.
      */
 
-    private static final long serialVersionUID = -387911632671998426L;
+    /**
+     * Base class for Iterators for LinkedBlockingDeque
+     */
+    private abstract class AbstractItr implements Iterator<E> {
+        /**
+         * The next node to return in next()
+         */
+         Node<E> next;
+
+        /**
+         * nextItem holds on to item fields because once we claim that
+         * an element exists in hasNext(), we must return item read
+         * under lock (in advance()) even if it was in the process of
+         * being removed when hasNext() was called.
+         */
+        E nextItem;
+
+        /**
+         * Node returned by most recent call to next. Needed by remove.
+         * Reset to null if this element is deleted by a call to remove.
+         */
+        private Node<E> lastRet;
+
+        /**
+         * Create a new iterator. Sets the initial position.
+         */
+        AbstractItr() {
+            // set to initial position
+            lock.lock();
+            try {
+                next = firstNode();
+                nextItem = next == null ? null : next.item;
+            } finally {
+                lock.unlock();
+            }
+        }
+
+        /**
+         * Advances next.
+         */
+        void advance() {
+            lock.lock();
+            try {
+                // assert next != null;
+                next = succ(next);
+                nextItem = next == null ? null : next.item;
+            } finally {
+                lock.unlock();
+            }
+        }
+
+        /**
+         * Obtain the first node to be returned by the iterator.
+         *
+         * @return first node
+         */
+        abstract Node<E> firstNode();
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public E next() {
+            if (next == null) {
+                throw new NoSuchElementException();
+            }
+            lastRet = next;
+            final E x = nextItem;
+            advance();
+            return x;
+        }
+
+        /**
+         * For a given node, obtain the next node to be returned by the
+         * iterator.
+         *
+         * @param n given node
+         *
+         * @return next node
+         */
+        abstract Node<E> nextNode(Node<E> n);
+
+        @Override
+        public void remove() {
+            final Node<E> n = lastRet;
+            if (n == null) {
+                throw new IllegalStateException();
+            }
+            lastRet = null;
+            lock.lock();
+            try {
+                if (n.item != null) {
+                    unlink(n);
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+
+        /**
+         * Returns the successor node of the given non-null, but
+         * possibly previously deleted, node.
+         *
+         * @param n node whose successor is sought
+         * @return successor node
+         */
+        private Node<E> succ(Node<E> n) {
+            // Chains of deleted nodes ending in null or self-links
+            // are possible if multiple interior nodes are removed.
+            for (;;) {
+                final Node<E> s = nextNode(n);
+                if (s == null) {
+                    return null;
+                } else if (s.item != null) {
+                    return s;
+                } else if (s == n) {
+                    return firstNode();
+                } else {
+                    n = s;
+                }
+            }
+        }
+    }
+
+    /** Descending iterator */
+    private class DescendingItr extends AbstractItr {
+        @Override
+        Node<E> firstNode() { return last; }
+        @Override
+        Node<E> nextNode(final Node<E> n) { return n.prev; }
+    }
+
+    /** Forward iterator */
+    private class Itr extends AbstractItr {
+        @Override
+        Node<E> firstNode() { return first; }
+        @Override
+        Node<E> nextNode(final Node<E> n) { return n.next; }
+        }
 
     /**
      * Doubly-linked list node class.
@@ -134,6 +275,8 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
         }
     }
 
+    private static final long serialVersionUID = -387911632671998426L;
+
     /**
      * Pointer to first node.
      * Invariant: (first == null && last == null) ||
@@ -181,6 +324,34 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
         this(Integer.MAX_VALUE, fairness);
     }
 
+
+    // Basic linking and unlinking operations, called only while holding lock
+
+    /**
+     * Creates a {@code LinkedBlockingDeque} with a capacity of
+     * {@link Integer#MAX_VALUE}, initially containing the elements of
+     * the given collection, added in traversal order of the
+     * collection's iterator.
+     *
+     * @param c the collection of elements to initially contain
+     * @throws NullPointerException if the specified collection or any
+     *         of its elements are null
+     */
+    public LinkedBlockingDeque(final Collection<? extends E> c) {
+        this(Integer.MAX_VALUE);
+        lock.lock(); // Never contended, but necessary for visibility
+        try {
+            for (final E e : c) {
+                Objects.requireNonNull(e);
+                if (!linkLast(e)) {
+                    throw new IllegalStateException("Deque full");
+                }
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
     /**
      * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity.
      *
@@ -211,480 +382,477 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
     }
 
     /**
-     * Creates a {@code LinkedBlockingDeque} with a capacity of
-     * {@link Integer#MAX_VALUE}, initially containing the elements of
-     * the given collection, added in traversal order of the
-     * collection's iterator.
-     *
-     * @param c the collection of elements to initially contain
-     * @throws NullPointerException if the specified collection or any
-     *         of its elements are null
+     * {@inheritDoc}
      */
-    public LinkedBlockingDeque(final Collection<? extends E> c) {
-        this(Integer.MAX_VALUE);
-        lock.lock(); // Never contended, but necessary for visibility
-        try {
-            for (final E e : c) {
-                Objects.requireNonNull(e);
-                if (!linkLast(e)) {
-                    throw new IllegalStateException("Deque full");
-                }
-            }
-        } finally {
-            lock.unlock();
-        }
+    @Override
+    public boolean add(final E e) {
+        addLast(e);
+        return true;
     }
 
-
-    // Basic linking and unlinking operations, called only while holding lock
-
     /**
-     * Links provided element as first element, or returns false if full.
-     *
-     * @param e The element to link as the first element.
-     *
-     * @return {@code true} if successful, otherwise {@code false}
+     * {@inheritDoc}
      */
-    private boolean linkFirst(final E e) {
-        // assert lock.isHeldByCurrentThread();
-        if (count >= capacity) {
-            return false;
-        }
-        final Node<E> f = first;
-        final Node<E> x = new Node<>(e, null, f);
-        first = x;
-        if (last == null) {
-            last = x;
-        } else {
-            f.prev = x;
+    @Override
+    public void addFirst(final E e) {
+        if (!offerFirst(e)) {
+            throw new IllegalStateException("Deque full");
         }
-        ++count;
-        notEmpty.signal();
-        return true;
     }
 
+    // BlockingDeque methods
+
     /**
-     * Links provided element as last element, or returns false if full.
-     *
-     * @param e The element to link as the last element.
-     *
-     * @return {@code true} if successful, otherwise {@code false}
+     * {@inheritDoc}
      */
-    private boolean linkLast(final E e) {
-        // assert lock.isHeldByCurrentThread();
-        if (count >= capacity) {
-            return false;
-        }
-        final Node<E> l = last;
-        final Node<E> x = new Node<>(e, l, null);
-        last = x;
-        if (first == null) {
-            first = x;
-        } else {
-            l.next = x;
+    @Override
+    public void addLast(final E e) {
+        if (!offerLast(e)) {
+            throw new IllegalStateException("Deque full");
         }
-        ++count;
-        notEmpty.signal();
-        return true;
     }
 
     /**
-     * Removes and returns the first element, or null if empty.
-     *
-     * @return The first element or {@code null} if empty
+     * Atomically removes all of the elements from this deque.
+     * The deque will be empty after this call returns.
      */
-    private E unlinkFirst() {
-        // assert lock.isHeldByCurrentThread();
-        final Node<E> f = first;
-        if (f == null) {
-            return null;
-        }
-        final Node<E> n = f.next;
-        final E item = f.item;
-        f.item = null;
-        f.next = f; // help GC
-        first = n;
-        if (n == null) {
-            last = null;
-        } else {
-            n.prev = null;
+    @Override
+    public void clear() {
+        lock.lock();
+        try {
+            for (Node<E> f = first; f != null;) {
+                f.item = null;
+                final Node<E> n = f.next;
+                f.prev = null;
+                f.next = null;
+                f = n;
+            }
+            first = last = null;
+            count = 0;
+            notFull.signalAll();
+        } finally {
+            lock.unlock();
         }
-        --count;
-        notFull.signal();
-        return item;
     }
 
     /**
-     * Removes and returns the last element, or null if empty.
+     * Returns {@code true} if this deque contains the specified element.
+     * More formally, returns {@code true} if and only if this deque contains
+     * at least one element {@code e} such that {@code o.equals(e)}.
      *
-     * @return The first element or {@code null} if empty
+     * @param o object to be checked for containment in this deque
+     * @return {@code true} if this deque contains the specified element
      */
-    private E unlinkLast() {
-        // assert lock.isHeldByCurrentThread();
-        final Node<E> l = last;
-        if (l == null) {
-            return null;
+    @Override
+    public boolean contains(final Object o) {
+        if (o == null) {
+            return false;
         }
-        final Node<E> p = l.prev;
-        final E item = l.item;
-        l.item = null;
-        l.prev = l; // help GC
-        last = p;
-        if (p == null) {
-            first = null;
-        } else {
-            p.next = null;
+        lock.lock();
+        try {
+            for (Node<E> p = first; p != null; p = p.next) {
+                if (o.equals(p.item)) {
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            lock.unlock();
         }
-        --count;
-        notFull.signal();
-        return item;
     }
 
     /**
-     * Unlinks the provided node.
-     *
-     * @param x The node to unlink
+     * {@inheritDoc}
      */
-    private void unlink(final Node<E> x) {
-        // assert lock.isHeldByCurrentThread();
-        final Node<E> p = x.prev;
-        final Node<E> n = x.next;
-        if (p == null) {
-            unlinkFirst();
-        } else if (n == null) {
-            unlinkLast();
-        } else {
-            p.next = n;
-            n.prev = p;
-            x.item = null;
-            // Don't mess with x's links.  They may still be in use by
-            // an iterator.
-        --count;
-            notFull.signal();
-        }
+    @Override
+    public Iterator<E> descendingIterator() {
+        return new DescendingItr();
     }
 
-    // BlockingDeque methods
+    /**
+     * Drains the queue to the specified collection.
+     *
+     * @param c The collection to add the elements to
+     *
+     * @return number of elements added to the collection
+     *
+     * @throws UnsupportedOperationException if the add operation is not
+     *         supported by the specified collection
+     * @throws ClassCastException if the class of the elements held by this
+     *         collection prevents them from being added to the specified
+     *         collection
+     * @throws NullPointerException if c is null
+     * @throws IllegalArgumentException if c is this instance
+     */
+    public int drainTo(final Collection<? super E> c) {
+        return drainTo(c, Integer.MAX_VALUE);
+    }
 
     /**
-     * {@inheritDoc}
+     * Drains no more than the specified number of elements from the queue to the
+     * specified collection.
+     *
+     * @param c           collection to add the elements to
+     * @param maxElements maximum number of elements to remove from the queue
+     *
+     * @return number of elements added to the collection
+     * @throws UnsupportedOperationException if the add operation is not
+     *         supported by the specified collection
+     * @throws ClassCastException if the class of the elements held by this
+     *         collection prevents them from being added to the specified
+     *         collection
+     * @throws NullPointerException if c is null
+     * @throws IllegalArgumentException if c is this instance
      */
-    @Override
-    public void addFirst(final E e) {
-        if (!offerFirst(e)) {
-            throw new IllegalStateException("Deque full");
+    public int drainTo(final Collection<? super E> c, final int maxElements) {
+        Objects.requireNonNull(c, "c");
+        if (c == this) {
+            throw new IllegalArgumentException();
+        }
+        lock.lock();
+        try {
+            final int n = Math.min(maxElements, count);
+            for (int i = 0; i < n; i++) {
+                c.add(first.item);   // In this order, in case add() throws.
+                unlinkFirst();
+            }
+            return n;
+        } finally {
+            lock.unlock();
         }
     }
 
     /**
-     * {@inheritDoc}
+     * Retrieves, but does not remove, the head of the queue represented by
+     * this deque.  This method differs from {@link #peek peek} only in that
+     * it throws an exception if this deque is empty.
+     *
+     * <p>This method is equivalent to {@link #getFirst() getFirst}.
+     *
+     * @return the head of the queue represented by this deque
+     * @throws NoSuchElementException if this deque is empty
      */
     @Override
-    public void addLast(final E e) {
-        if (!offerLast(e)) {
-            throw new IllegalStateException("Deque full");
-        }
+    public E element() {
+        return getFirst();
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public boolean offerFirst(final E e) {
-        Objects.requireNonNull(e, "e");
-        lock.lock();
-        try {
-            return linkFirst(e);
-        } finally {
-            lock.unlock();
+    public E getFirst() {
+        final E x = peekFirst();
+        if (x == null) {
+            throw new NoSuchElementException();
         }
+        return x;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public boolean offerLast(final E e) {
-        Objects.requireNonNull(e, "e");
-        lock.lock();
-        try {
-            return linkLast(e);
-        } finally {
-            lock.unlock();
+    public E getLast() {
+        final E x = peekLast();
+        if (x == null) {
+            throw new NoSuchElementException();
         }
+        return x;
     }
 
     /**
-     * Links the provided element as the first in the queue, waiting until there
-     * is space to do so if the queue is full.
-     *
-     * @param e element to link
+     * Returns the length of the queue of threads waiting to take instances from this deque. See disclaimer on accuracy
+     * in {@link java.util.concurrent.locks.ReentrantLock#getWaitQueueLength(Condition)}.
      *
-     * @throws NullPointerException if e is null
-     * @throws InterruptedException if the thread is interrupted whilst waiting
-     *         for space
+     * @return number of threads waiting on this deque's notEmpty condition.
      */
-    public void putFirst(final E e) throws InterruptedException {
-        Objects.requireNonNull(e, "e");
+    public int getTakeQueueLength() {
         lock.lock();
         try {
-            while (!linkFirst(e)) {
-                notFull.await();
-            }
+           return lock.getWaitQueueLength(notEmpty);
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Links the provided element as the last in the queue, waiting until there
-     * is space to do so if the queue is full.
-     *
-     * @param e element to link
+     * Returns true if there are threads waiting to take instances from this deque. See disclaimer on accuracy in
+     * {@link java.util.concurrent.locks.ReentrantLock#hasWaiters(Condition)}.
      *
-     * @throws NullPointerException if e is null
-     * @throws InterruptedException if the thread is interrupted whilst waiting
-     *         for space
+     * @return true if there is at least one thread waiting on this deque's notEmpty condition.
      */
-    public void putLast(final E e) throws InterruptedException {
-        Objects.requireNonNull(e, "e");
+    public boolean hasTakeWaiters() {
         lock.lock();
         try {
-            while (!linkLast(e)) {
-                notFull.await();
-            }
+            return lock.hasWaiters(notEmpty);
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Links the provided element as the first in the queue, waiting up to the
-     * specified time to do so if the queue is full.
-     *
-     * @param e         element to link
-     * @param timeout   length of time to wait
-     * @param unit      units that timeout is expressed in
-     *
-     * @return {@code true} if successful, otherwise {@code false}
-     *
-     * @throws NullPointerException if e is null
-     * @throws InterruptedException if the thread is interrupted whilst waiting
-     *         for space
+     * Interrupts the threads currently waiting to take an object from the pool. See disclaimer on accuracy in
+     * {@link java.util.concurrent.locks.ReentrantLock#getWaitingThreads(Condition)}.
      */
-    public boolean offerFirst(final E e, final long timeout, final TimeUnit unit)
-        throws InterruptedException {
-        Objects.requireNonNull(e, "e");
-        long nanos = unit.toNanos(timeout);
-        lock.lockInterruptibly();
+    public void interuptTakeWaiters() {
+        lock.lock();
         try {
-            while (!linkFirst(e)) {
-                if (nanos <= 0) {
-                    return false;
-                }
-                nanos = notFull.awaitNanos(nanos);
-            }
-            return true;
+           lock.interruptWaiters(notEmpty);
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Links the provided element as the last in the queue, waiting up to the
-     * specified time to do so if the queue is full.
-     *
-     * @param e         element to link
-     * @param timeout   length of time to wait
-     * @param unit      units that timeout is expressed in
+     * Returns an iterator over the elements in this deque in proper sequence.
+     * The elements will be returned in order from first (head) to last (tail).
+     * The returned {@code Iterator} is a "weakly consistent" iterator that
+     * will never throw {@link java.util.ConcurrentModificationException
+     * ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
      *
-     * @return {@code true} if successful, otherwise {@code false}
+     * @return an iterator over the elements in this deque in proper sequence
+     */
+    @Override
+    public Iterator<E> iterator() {
+        return new Itr();
+    }
+
+    /**
+     * Links provided element as first element, or returns false if full.
      *
-     * @throws NullPointerException if e is null
-     * @throws InterruptedException if the thread is interrupted whist waiting
-     *         for space
+     * @param e The element to link as the first element.
+     *
+     * @return {@code true} if successful, otherwise {@code false}
      */
-    public boolean offerLast(final E e, final long timeout, final TimeUnit unit)
-        throws InterruptedException {
-        Objects.requireNonNull(e, "e");
-        long nanos = unit.toNanos(timeout);
-        lock.lockInterruptibly();
-        try {
-            while (!linkLast(e)) {
-                if (nanos <= 0) {
-                    return false;
-                }
-                nanos = notFull.awaitNanos(nanos);
-            }
-            return true;
-        } finally {
-            lock.unlock();
+    private boolean linkFirst(final E e) {
+        // assert lock.isHeldByCurrentThread();
+        if (count >= capacity) {
+            return false;
+        }
+        final Node<E> f = first;
+        final Node<E> x = new Node<>(e, null, f);
+        first = x;
+        if (last == null) {
+            last = x;
+        } else {
+            f.prev = x;
         }
+        ++count;
+        notEmpty.signal();
+        return true;
     }
 
     /**
-     * {@inheritDoc}
+     * Links provided element as last element, or returns false if full.
+     *
+     * @param e The element to link as the last element.
+     *
+     * @return {@code true} if successful, otherwise {@code false}
      */
-    @Override
-    public E removeFirst() {
-        final E x = pollFirst();
-        if (x == null) {
-            throw new NoSuchElementException();
+    private boolean linkLast(final E e) {
+        // assert lock.isHeldByCurrentThread();
+        if (count >= capacity) {
+            return false;
         }
-        return x;
+        final Node<E> l = last;
+        final Node<E> x = new Node<>(e, l, null);
+        last = x;
+        if (first == null) {
+            first = x;
+        } else {
+            l.next = x;
+        }
+        ++count;
+        notEmpty.signal();
+        return true;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public E removeLast() {
-        final E x = pollLast();
-        if (x == null) {
-            throw new NoSuchElementException();
-        }
-        return x;
+    public boolean offer(final E e) {
+        return offerLast(e);
     }
 
-    @Override
-    public E pollFirst() {
-        lock.lock();
-        try {
-            return unlinkFirst();
-        } finally {
-            lock.unlock();
-        }
+    /**
+     * Links the provided element as the last in the queue, waiting up to the
+     * specified time to do so if the queue is full.
+     * <p>
+     * This method is equivalent to {@link #offerLast(Object, long, TimeUnit)}
+     *
+     * @param e         element to link
+     * @param timeout   length of time to wait
+     *
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whilst waiting
+     *         for space
+     */
+    boolean offer(final E e, final Duration timeout)
+        throws InterruptedException {
+        return offerLast(e, timeout);
     }
 
-    @Override
-    public E pollLast() {
-        lock.lock();
-        try {
-            return unlinkLast();
-        } finally {
-            lock.unlock();
-        }
+    /**
+     * Links the provided element as the last in the queue, waiting up to the
+     * specified time to do so if the queue is full.
+     * <p>
+     * This method is equivalent to {@link #offerLast(Object, long, TimeUnit)}
+     *
+     * @param e         element to link
+     * @param timeout   length of time to wait
+     * @param unit      units that timeout is expressed in
+     *
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whilst waiting
+     *         for space
+     */
+    public boolean offer(final E e, final long timeout, final TimeUnit unit)
+        throws InterruptedException {
+        return offerLast(e, timeout, unit);
     }
 
     /**
-     * Unlinks the first element in the queue, waiting until there is an element
-     * to unlink if the queue is empty.
-     *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
+     * {@inheritDoc}
      */
-    public E takeFirst() throws InterruptedException {
+    @Override
+    public boolean offerFirst(final E e) {
+        Objects.requireNonNull(e, "e");
         lock.lock();
         try {
-            E x;
-            while ( (x = unlinkFirst()) == null) {
-                notEmpty.await();
-            }
-            return x;
+            return linkFirst(e);
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Unlinks the last element in the queue, waiting until there is an element
-     * to unlink if the queue is empty.
+     * Links the provided element as the first in the queue, waiting up to the
+     * specified time to do so if the queue is full.
      *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
+     * @param e         element to link
+     * @param timeout   length of time to wait
+     *
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whilst waiting
+     *         for space
      */
-    public E takeLast() throws InterruptedException {
-        lock.lock();
+    public boolean offerFirst(final E e, final Duration timeout)
+        throws InterruptedException {
+        Objects.requireNonNull(e, "e");
+        long nanos = timeout.toNanos();
+        lock.lockInterruptibly();
         try {
-            E x;
-            while ( (x = unlinkLast()) == null) {
-                notEmpty.await();
+            while (!linkFirst(e)) {
+                if (nanos <= 0) {
+                    return false;
+                }
+                nanos = notFull.awaitNanos(nanos);
             }
-            return x;
+            return true;
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Unlinks the first element in the queue, waiting up to the specified time
-     * to do so if the queue is empty.
+     * Links the provided element as the first in the queue, waiting up to the
+     * specified time to do so if the queue is full.
      *
+     * @param e         element to link
      * @param timeout   length of time to wait
      * @param unit      units that timeout is expressed in
      *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whilst waiting
+     *         for space
      */
-    public E pollFirst(final long timeout, final TimeUnit unit)
+    public boolean offerFirst(final E e, final long timeout, final TimeUnit unit)
         throws InterruptedException {
-        long nanos = unit.toNanos(timeout);
-        lock.lockInterruptibly();
+        return offerFirst(e, PoolImplUtils.toDuration(timeout, unit));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean offerLast(final E e) {
+        Objects.requireNonNull(e, "e");
+        lock.lock();
         try {
-            E x;
-            while ( (x = unlinkFirst()) == null) {
-                if (nanos <= 0) {
-                    return null;
-                }
-                nanos = notEmpty.awaitNanos(nanos);
-            }
-            return x;
+            return linkLast(e);
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Unlinks the last element in the queue, waiting up to the specified time
-     * to do so if the queue is empty.
+     * Links the provided element as the last in the queue, waiting up to the
+     * specified time to do so if the queue is full.
      *
+     * @param e         element to link
      * @param timeout   length of time to wait
-     * @param unit      units that timeout is expressed in
      *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whist waiting
+     *         for space
      */
-    public E pollLast(final long timeout, final TimeUnit unit)
+    boolean offerLast(final E e, final Duration timeout)
         throws InterruptedException {
-        long nanos = unit.toNanos(timeout);
+        Objects.requireNonNull(e, "e");
+        long nanos = timeout.toNanos();
         lock.lockInterruptibly();
         try {
-            E x;
-            while ( (x = unlinkLast()) == null) {
+            while (!linkLast(e)) {
                 if (nanos <= 0) {
-                    return null;
+                    return false;
                 }
-                nanos = notEmpty.awaitNanos(nanos);
+                nanos = notFull.awaitNanos(nanos);
             }
-            return x;
+            return true;
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * {@inheritDoc}
+     * Links the provided element as the last in the queue, waiting up to the
+     * specified time to do so if the queue is full.
+     *
+     * @param e         element to link
+     * @param timeout   length of time to wait
+     * @param unit      units that timeout is expressed in
+     *
+     * @return {@code true} if successful, otherwise {@code false}
+     *
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whist waiting
+     *         for space
      */
+    public boolean offerLast(final E e, final long timeout, final TimeUnit unit) throws InterruptedException {
+        return offerLast(e, PoolImplUtils.toDuration(timeout, unit));
+    }
+
     @Override
-    public E getFirst() {
-        final E x = peekFirst();
-        if (x == null) {
-            throw new NoSuchElementException();
-        }
-        return x;
+    public E peek() {
+        return peekFirst();
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public E getLast() {
-        final E x = peekLast();
-        if (x == null) {
-            throw new NoSuchElementException();
-        }
-        return x;
-    }
+    // BlockingQueue methods
 
     @Override
     public E peekFirst() {
@@ -707,60 +875,157 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
     }
 
     @Override
-    public boolean removeFirstOccurrence(final Object o) {
-        if (o == null) {
-            return false;
-        }
+    public E poll() {
+        return pollFirst();
+    }
+
+    /**
+     * Unlinks the first element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * <p>This method is equivalent to {@link #pollFirst(long, TimeUnit)}.
+     *
+     * @param timeout   length of time to wait
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    E poll(final Duration timeout) throws InterruptedException {
+        return pollFirst(timeout);
+    }
+
+    /**
+     * Unlinks the first element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * <p>This method is equivalent to {@link #pollFirst(long, TimeUnit)}.
+     *
+     * @param timeout   length of time to wait
+     * @param unit      units that timeout is expressed in
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E poll(final long timeout, final TimeUnit unit) throws InterruptedException {
+        return pollFirst(timeout, unit);
+    }
+
+    @Override
+    public E pollFirst() {
         lock.lock();
         try {
-            for (Node<E> p = first; p != null; p = p.next) {
-                if (o.equals(p.item)) {
-                    unlink(p);
-                    return true;
+            return unlinkFirst();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Unlinks the first element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * @param timeout   length of time to wait
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    E pollFirst(final Duration timeout) throws InterruptedException {
+        long nanos = timeout.toNanos();
+        lock.lockInterruptibly();
+        try {
+            E x;
+            while ((x = unlinkFirst()) == null) {
+                if (nanos <= 0) {
+                    return null;
                 }
+                nanos = notEmpty.awaitNanos(nanos);
             }
-            return false;
+            return x;
         } finally {
             lock.unlock();
         }
     }
 
+    /**
+     * Unlinks the first element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * @param timeout   length of time to wait
+     * @param unit      units that timeout is expressed in
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E pollFirst(final long timeout, final TimeUnit unit) throws InterruptedException {
+        return pollFirst(PoolImplUtils.toDuration(timeout, unit));
+    }
+
     @Override
-    public boolean removeLastOccurrence(final Object o) {
-        if (o == null) {
-            return false;
-        }
+    public E pollLast() {
         lock.lock();
         try {
-            for (Node<E> p = last; p != null; p = p.prev) {
-                if (o.equals(p.item)) {
-                    unlink(p);
-                    return true;
+            return unlinkLast();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Unlinks the last element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * @param timeout   length of time to wait
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E pollLast(final Duration timeout)
+        throws InterruptedException {
+        long nanos = timeout.toNanos();
+        lock.lockInterruptibly();
+        try {
+            E x;
+            while ( (x = unlinkLast()) == null) {
+                if (nanos <= 0) {
+                    return null;
                 }
+                nanos = notEmpty.awaitNanos(nanos);
             }
-            return false;
+            return x;
         } finally {
             lock.unlock();
         }
     }
 
-    // BlockingQueue methods
+    /**
+     * Unlinks the last element in the queue, waiting up to the specified time
+     * to do so if the queue is empty.
+     *
+     * @param timeout   length of time to wait
+     * @param unit      units that timeout is expressed in
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E pollLast(final long timeout, final TimeUnit unit)
+        throws InterruptedException {
+        return pollLast(PoolImplUtils.toDuration(timeout, unit));
+    }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public boolean add(final E e) {
-        addLast(e);
-        return true;
+    public E pop() {
+        return removeFirst();
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public boolean offer(final E e) {
-        return offerLast(e);
+    public void push(final E e) {
+        addFirst(e);
     }
 
     /**
@@ -780,26 +1045,98 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
     }
 
     /**
-     * Links the provided element as the last in the queue, waiting up to the
-     * specified time to do so if the queue is full.
-     * <p>
-     * This method is equivalent to {@link #offerLast(Object, long, TimeUnit)}
+     * Links the provided element as the first in the queue, waiting until there
+     * is space to do so if the queue is full.
      *
-     * @param e         element to link
-     * @param timeout   length of time to wait
-     * @param unit      units that timeout is expressed in
+     * @param e element to link
      *
-     * @return {@code true} if successful, otherwise {@code false}
+     * @throws NullPointerException if e is null
+     * @throws InterruptedException if the thread is interrupted whilst waiting
+     *         for space
+     */
+    public void putFirst(final E e) throws InterruptedException {
+        Objects.requireNonNull(e, "e");
+        lock.lock();
+        try {
+            while (!linkFirst(e)) {
+                notFull.await();
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Links the provided element as the last in the queue, waiting until there
+     * is space to do so if the queue is full.
+     *
+     * @param e element to link
      *
      * @throws NullPointerException if e is null
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
-    public boolean offer(final E e, final long timeout, final TimeUnit unit)
-        throws InterruptedException {
-        return offerLast(e, timeout, unit);
+    public void putLast(final E e) throws InterruptedException {
+        Objects.requireNonNull(e, "e");
+        lock.lock();
+        try {
+            while (!linkLast(e)) {
+                notFull.await();
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    // Stack methods
+
+    /**
+     * Reconstitutes this deque from a stream (that is,
+     * deserialize it).
+     * @param s the stream
+     */
+    private void readObject(final java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        s.defaultReadObject();
+        count = 0;
+        first = null;
+        last = null;
+        // Read in all elements and place in queue
+        for (;;) {
+            @SuppressWarnings("unchecked")
+            final
+            E item = (E)s.readObject();
+            if (item == null) {
+                break;
+            }
+            add(item);
+        }
+    }
+
+    /**
+     * Returns the number of additional elements that this deque can ideally
+     * (in the absence of memory or resource constraints) accept without
+     * blocking. This is always equal to the initial capacity of this deque
+     * less the current {@code size} of this deque.
+     *
+     * <p>Note that you <em>cannot</em> always tell if an attempt to insert
+     * an element will succeed by inspecting {@code remainingCapacity}
+     * because it may be the case that another thread is about to
+     * insert or remove an element.
+     *
+     * @return The number of additional elements the queue is able to accept
+     */
+    public int remainingCapacity() {
+        lock.lock();
+        try {
+            return capacity - count;
+        } finally {
+            lock.unlock();
+        }
     }
 
+    // Collection methods
+
     /**
      * Retrieves and removes the head of the queue represented by this deque.
      * This method differs from {@link #poll poll} only in that it throws an
@@ -815,155 +1152,6 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
         return removeFirst();
     }
 
-    @Override
-    public E poll() {
-        return pollFirst();
-    }
-
-    /**
-     * Unlinks the first element in the queue, waiting until there is an element
-     * to unlink if the queue is empty.
-     *
-     * <p>This method is equivalent to {@link #takeFirst()}.
-     *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
-     */
-    public E take() throws InterruptedException {
-        return takeFirst();
-    }
-
-    /**
-     * Unlinks the first element in the queue, waiting up to the specified time
-     * to do so if the queue is empty.
-     *
-     * <p>This method is equivalent to {@link #pollFirst(long, TimeUnit)}.
-     *
-     * @param timeout   length of time to wait
-     * @param unit      units that timeout is expressed in
-     *
-     * @return the unlinked element
-     * @throws InterruptedException if the current thread is interrupted
-     */
-    public E poll(final long timeout, final TimeUnit unit) throws InterruptedException {
-        return pollFirst(timeout, unit);
-    }
-
-    /**
-     * Retrieves, but does not remove, the head of the queue represented by
-     * this deque.  This method differs from {@link #peek peek} only in that
-     * it throws an exception if this deque is empty.
-     *
-     * <p>This method is equivalent to {@link #getFirst() getFirst}.
-     *
-     * @return the head of the queue represented by this deque
-     * @throws NoSuchElementException if this deque is empty
-     */
-    @Override
-    public E element() {
-        return getFirst();
-    }
-
-    @Override
-    public E peek() {
-        return peekFirst();
-    }
-
-    /**
-     * Returns the number of additional elements that this deque can ideally
-     * (in the absence of memory or resource constraints) accept without
-     * blocking. This is always equal to the initial capacity of this deque
-     * less the current {@code size} of this deque.
-     *
-     * <p>Note that you <em>cannot</em> always tell if an attempt to insert
-     * an element will succeed by inspecting {@code remainingCapacity}
-     * because it may be the case that another thread is about to
-     * insert or remove an element.
-     *
-     * @return The number of additional elements the queue is able to accept
-     */
-    public int remainingCapacity() {
-        lock.lock();
-        try {
-            return capacity - count;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    /**
-     * Drains the queue to the specified collection.
-     *
-     * @param c The collection to add the elements to
-     *
-     * @return number of elements added to the collection
-     *
-     * @throws UnsupportedOperationException if the add operation is not
-     *         supported by the specified collection
-     * @throws ClassCastException if the class of the elements held by this
-     *         collection prevents them from being added to the specified
-     *         collection
-     * @throws NullPointerException if c is null
-     * @throws IllegalArgumentException if c is this instance
-     */
-    public int drainTo(final Collection<? super E> c) {
-        return drainTo(c, Integer.MAX_VALUE);
-    }
-
-    /**
-     * Drains no more than the specified number of elements from the queue to the
-     * specified collection.
-     *
-     * @param c           collection to add the elements to
-     * @param maxElements maximum number of elements to remove from the queue
-     *
-     * @return number of elements added to the collection
-     * @throws UnsupportedOperationException if the add operation is not
-     *         supported by the specified collection
-     * @throws ClassCastException if the class of the elements held by this
-     *         collection prevents them from being added to the specified
-     *         collection
-     * @throws NullPointerException if c is null
-     * @throws IllegalArgumentException if c is this instance
-     */
-    public int drainTo(final Collection<? super E> c, final int maxElements) {
-        Objects.requireNonNull(c, "c");
-        if (c == this) {
-            throw new IllegalArgumentException();
-        }
-        lock.lock();
-        try {
-            final int n = Math.min(maxElements, count);
-            for (int i = 0; i < n; i++) {
-                c.add(first.item);   // In this order, in case add() throws.
-                unlinkFirst();
-            }
-            return n;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    // Stack methods
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void push(final E e) {
-        addFirst(e);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public E pop() {
-        return removeFirst();
-    }
-
-    // Collection methods
-
     /**
      * Removes the first occurrence of the specified element from this deque.
      * If the deque does not contain the element, it is unchanged.
@@ -984,44 +1172,15 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
     }
 
     /**
-     * Returns the number of elements in this deque.
-     *
-     * @return the number of elements in this deque
-     */
-    @Override
-    public int size() {
-        lock.lock();
-        try {
-            return count;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    /**
-     * Returns {@code true} if this deque contains the specified element.
-     * More formally, returns {@code true} if and only if this deque contains
-     * at least one element {@code e} such that {@code o.equals(e)}.
-     *
-     * @param o object to be checked for containment in this deque
-     * @return {@code true} if this deque contains the specified element
+     * {@inheritDoc}
      */
     @Override
-    public boolean contains(final Object o) {
-        if (o == null) {
-            return false;
-        }
-        lock.lock();
-        try {
-            for (Node<E> p = first; p != null; p = p.next) {
-                if (o.equals(p.item)) {
-                    return true;
-                }
-            }
-            return false;
-        } finally {
-            lock.unlock();
+    public E removeFirst() {
+        final E x = pollFirst();
+        if (x == null) {
+            throw new NoSuchElementException();
         }
+        return x;
     }
 
     /*
@@ -1065,29 +1224,20 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
 //         }
 //     }
 
-    /**
-     * Returns an array containing all of the elements in this deque, in
-     * proper sequence (from first to last element).
-     *
-     * <p>The returned array will be "safe" in that no references to it are
-     * maintained by this deque.  (In other words, this method must allocate
-     * a new array).  The caller is thus free to modify the returned array.
-     *
-     * <p>This method acts as bridge between array-based and collection-based
-     * APIs.
-     *
-     * @return an array containing all of the elements in this deque
-     */
     @Override
-    public Object[] toArray() {
+    public boolean removeFirstOccurrence(final Object o) {
+        if (o == null) {
+            return false;
+        }
         lock.lock();
         try {
-            final Object[] a = new Object[count];
-            int k = 0;
             for (Node<E> p = first; p != null; p = p.next) {
-                a[k++] = p.item;
+                if (o.equals(p.item)) {
+                    unlink(p);
+                    return true;
+                }
             }
-            return a;
+            return false;
         } finally {
             lock.unlock();
         }
@@ -1096,315 +1246,262 @@ class LinkedBlockingDeque<E> extends AbstractQueue<E>
     /**
      * {@inheritDoc}
      */
-    @SuppressWarnings("unchecked")
     @Override
-    public <T> T[] toArray(T[] a) {
+    public E removeLast() {
+        final E x = pollLast();
+        if (x == null) {
+            throw new NoSuchElementException();
+        }
+        return x;
+    }
+
+    @Override
+    public boolean removeLastOccurrence(final Object o) {
+        if (o == null) {
+            return false;
+        }
         lock.lock();
         try {
-            if (a.length < count) {
-                a = (T[])java.lang.reflect.Array.newInstance
-                    (a.getClass().getComponentType(), count);
-            }
-            int k = 0;
-            for (Node<E> p = first; p != null; p = p.next) {
-                a[k++] = (T)p.item;
-            }
-            if (a.length > k) {
-                a[k] = null;
+            for (Node<E> p = last; p != null; p = p.prev) {
+                if (o.equals(p.item)) {
+                    unlink(p);
+                    return true;
+                }
             }
-            return a;
+            return false;
         } finally {
             lock.unlock();
         }
     }
 
+    /**
+     * Returns the number of elements in this deque.
+     *
+     * @return the number of elements in this deque
+     */
     @Override
-    public String toString() {
+    public int size() {
         lock.lock();
         try {
-            return super.toString();
+            return count;
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Atomically removes all of the elements from this deque.
-     * The deque will be empty after this call returns.
-     */
-    @Override
-    public void clear() {
+     * Unlinks the first element in the queue, waiting until there is an element
+     * to unlink if the queue is empty.
+     *
+     * <p>This method is equivalent to {@link #takeFirst()}.
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E take() throws InterruptedException {
+        return takeFirst();
+    }
+
+    /**
+     * Unlinks the first element in the queue, waiting until there is an element
+     * to unlink if the queue is empty.
+     *
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
+     */
+    public E takeFirst() throws InterruptedException {
         lock.lock();
         try {
-            for (Node<E> f = first; f != null;) {
-                f.item = null;
-                final Node<E> n = f.next;
-                f.prev = null;
-                f.next = null;
-                f = n;
+            E x;
+            while ( (x = unlinkFirst()) == null) {
+                notEmpty.await();
             }
-            first = last = null;
-            count = 0;
-            notFull.signalAll();
+            return x;
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Returns an iterator over the elements in this deque in proper sequence.
-     * The elements will be returned in order from first (head) to last (tail).
-     * The returned {@code Iterator} is a "weakly consistent" iterator that
-     * will never throw {@link java.util.ConcurrentModificationException
-     * ConcurrentModificationException},
-     * and guarantees to traverse elements as they existed upon
-     * construction of the iterator, and may (but is not guaranteed to)
-     * reflect any modifications subsequent to construction.
+     * Unlinks the last element in the queue, waiting until there is an element
+     * to unlink if the queue is empty.
      *
-     * @return an iterator over the elements in this deque in proper sequence
+     * @return the unlinked element
+     * @throws InterruptedException if the current thread is interrupted
      */
-    @Override
-    public Iterator<E> iterator() {
-        return new Itr();
+    public E takeLast() throws InterruptedException {
+        lock.lock();
+        try {
+            E x;
+            while ( (x = unlinkLast()) == null) {
+                notEmpty.await();
+            }
+            return x;
+        } finally {
+            lock.unlock();
+        }
     }
 
     /**
-     * {@inheritDoc}
+     * Returns an array containing all of the elements in this deque, in
+     * proper sequence (from first to last element).
+     *
+     * <p>The returned array will be "safe" in that no references to it are
+     * maintained by this deque.  (In other words, this method must allocate
+     * a new array).  The caller is thus free to modify the returned array.
+     *
+     * <p>This method acts as bridge between array-based and collection-based
+     * APIs.
+     *
+     * @return an array containing all of the elements in this deque
      */
     @Override
-    public Iterator<E> descendingIterator() {
-        return new DescendingItr();
+    public Object[] toArray() {
+        lock.lock();
+        try {
+            final Object[] a = new Object[count];
+            int k = 0;
+            for (Node<E> p = first; p != null; p = p.next) {
+                a[k++] = p.item;
+            }
+            return a;
+        } finally {
+            lock.unlock();
+        }
     }
 
     /**
-     * Base class for Iterators for LinkedBlockingDeque
+     * {@inheritDoc}
      */
-    private abstract class AbstractItr implements Iterator<E> {
-        /**
-         * The next node to return in next()
-         */
-         Node<E> next;
-
-        /**
-         * nextItem holds on to item fields because once we claim that
-         * an element exists in hasNext(), we must return item read
-         * under lock (in advance()) even if it was in the process of
-         * being removed when hasNext() was called.
-         */
-        E nextItem;
-
-        /**
-         * Node returned by most recent call to next. Needed by remove.
-         * Reset to null if this element is deleted by a call to remove.
-         */
-        private Node<E> lastRet;
-
-        /**
-         * Obtain the first node to be returned by the iterator.
-         *
-         * @return first node
-         */
-        abstract Node<E> firstNode();
-
-        /**
-         * For a given node, obtain the next node to be returned by the
-         * iterator.
-         *
-         * @param n given node
-         *
-         * @return next node
-         */
-        abstract Node<E> nextNode(Node<E> n);
-
-        /**
-         * Create a new iterator. Sets the initial position.
-         */
-        AbstractItr() {
-            // set to initial position
-            lock.lock();
-            try {
-                next = firstNode();
-                nextItem = next == null ? null : next.item;
-            } finally {
-                lock.unlock();
-            }
-        }
-
-        /**
-         * Returns the successor node of the given non-null, but
-         * possibly previously deleted, node.
-         *
-         * @param n node whose successor is sought
-         * @return successor node
-         */
-        private Node<E> succ(Node<E> n) {
-            // Chains of deleted nodes ending in null or self-links
-            // are possible if multiple interior nodes are removed.
-            for (;;) {
-                final Node<E> s = nextNode(n);
-                if (s == null) {
-                    return null;
-                } else if (s.item != null) {
-                    return s;
-                } else if (s == n) {
-                    return firstNode();
-                } else {
-                    n = s;
-                }
-            }
-        }
-
-        /**
-         * Advances next.
-         */
-        void advance() {
-            lock.lock();
-            try {
-                // assert next != null;
-                next = succ(next);
-                nextItem = next == null ? null : next.item;
-            } finally {
-                lock.unlock();
-            }
-        }
-
-        @Override
-        public boolean hasNext() {
-            return next != null;
-        }
-
-        @Override
-        public E next() {
-            if (next == null) {
-                throw new NoSuchElementException();
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T[] toArray(T[] a) {
+        lock.lock();
+        try {
+            if (a.length < count) {
+                a = (T[])java.lang.reflect.Array.newInstance
+                    (a.getClass().getComponentType(), count);
             }
-            lastRet = next;
-            final E x = nextItem;
-            advance();
-            return x;
-        }
-
-        @Override
-        public void remove() {
-            final Node<E> n = lastRet;
-            if (n == null) {
-                throw new IllegalStateException();
+            int k = 0;
+            for (Node<E> p = first; p != null; p = p.next) {
+                a[k++] = (T)p.item;
             }
-            lastRet = null;
-            lock.lock();
-            try {
-                if (n.item != null) {
-                    unlink(n);
-                }
-            } finally {
-                lock.unlock();
+            if (a.length > k) {
+                a[k] = null;
             }
+            return a;
+        } finally {
+            lock.unlock();
         }
     }
 
-    /** Forward iterator */
-    private class Itr extends AbstractItr {
-        @Override
-        Node<E> firstNode() { return first; }
-        @Override
-        Node<E> nextNode(final Node<E> n) { return n.next; }
-        }
-
-    /** Descending iterator */
-    private class DescendingItr extends AbstractItr {
-        @Override
-        Node<E> firstNode() { return last; }
-        @Override
-        Node<E> nextNode(final Node<E> n) { return n.prev; }
-    }
-
-    /**
-     * Saves the state of this deque to a stream (that is, serialize it).
-     *
-     * @serialData The capacity (int), followed by elements (each an
-     * {@code Object}) in the proper order, followed by a null
-     * @param s the stream
-     */
-    private void writeObject(final java.io.ObjectOutputStream s)
-        throws java.io.IOException {
+    @Override
+    public String toString() {
         lock.lock();
         try {
-            // Write out capacity and any hidden stuff
-            s.defaultWriteObject();
-            // Write out all elements in the proper order.
-            for (Node<E> p = first; p != null; p = p.next) {
-                s.writeObject(p.item);
-            }
-            // Use trailing null as sentinel
-            s.writeObject(null);
+            return super.toString();
         } finally {
             lock.unlock();
         }
     }
 
     /**
-     * Reconstitutes this deque from a stream (that is,
-     * deserialize it).
-     * @param s the stream
+     * Unlinks the provided node.
+     *
+     * @param x The node to unlink
      */
-    private void readObject(final java.io.ObjectInputStream s)
-        throws java.io.IOException, ClassNotFoundException {
-        s.defaultReadObject();
-        count = 0;
-        first = null;
-        last = null;
-        // Read in all elements and place in queue
-        for (;;) {
-            @SuppressWarnings("unchecked")
-            final
-            E item = (E)s.readObject();
-            if (item == null) {
-                break;
-            }
-            add(item);
+    private void unlink(final Node<E> x) {
+        // assert lock.isHeldByCurrentThread();
+        final Node<E> p = x.prev;
+        final Node<E> n = x.next;
+        if (p == null) {
+            unlinkFirst();
+        } else if (n == null) {
+            unlinkLast();
+        } else {
+            p.next = n;
+            n.prev = p;
+            x.item = null;
+            // Don't mess with x's links.  They may still be in use by
+            // an iterator.
+        --count;
+            notFull.signal();
         }
     }
 
     // Monitoring methods
 
     /**
-     * Returns true if there are threads waiting to take instances from this deque. See disclaimer on accuracy in
-     * {@link java.util.concurrent.locks.ReentrantLock#hasWaiters(Condition)}.
+     * Removes and returns the first element, or null if empty.
      *
-     * @return true if there is at least one thread waiting on this deque's notEmpty condition.
+     * @return The first element or {@code null} if empty
      */
-    public boolean hasTakeWaiters() {
-        lock.lock();
-        try {
-            return lock.hasWaiters(notEmpty);
-        } finally {
-            lock.unlock();
+    private E unlinkFirst() {
+        // assert lock.isHeldByCurrentThread();
+        final Node<E> f = first;
+        if (f == null) {
+            return null;
         }
+        final Node<E> n = f.next;
+        final E item = f.item;
+        f.item = null;
+        f.next = f; // help GC
+        first = n;
+        if (n == null) {
+            last = null;
+        } else {
+            n.prev = null;
+        }
+        --count;
+        notFull.signal();
+        return item;
     }
 
     /**
-     * Returns the length of the queue of threads waiting to take instances from this deque. See disclaimer on accuracy
-     * in {@link java.util.concurrent.locks.ReentrantLock#getWaitQueueLength(Condition)}.
+     * Removes and returns the last element, or null if empty.
      *
-     * @return number of threads waiting on this deque's notEmpty condition.
+     * @return The first element or {@code null} if empty
      */
-    public int getTakeQueueLength() {
-        lock.lock();
-        try {
-           return lock.getWaitQueueLength(notEmpty);
-        } finally {
-            lock.unlock();
+    private E unlinkLast() {
+        // assert lock.isHeldByCurrentThread();
+        final Node<E> l = last;
+        if (l == null) {
+            return null;
+        }
+        final Node<E> p = l.prev;
+        final E item = l.item;
+        l.item = null;
+        l.prev = l; // help GC
+        last = p;
+        if (p == null) {
+            first = null;
+        } else {
+            p.next = null;
         }
+        --count;
+        notFull.signal();
+        return item;
     }
 
     /**
-     * Interrupts the threads currently waiting to take an object from the pool. See disclaimer on accuracy in
-     * {@link java.util.concurrent.locks.ReentrantLock#getWaitingThreads(Condition)}.
+     * Saves the state of this deque to a stream (that is, serialize it).
+     *
+     * @serialData The capacity (int), followed by elements (each an
+     * {@code Object}) in the proper order, followed by a null
+     * @param s the stream
      */
-    public void interuptTakeWaiters() {
+    private void writeObject(final java.io.ObjectOutputStream s)
+        throws java.io.IOException {
         lock.lock();
         try {
-           lock.interruptWaiters(notEmpty);
+            // Write out capacity and any hidden stuff
+            s.defaultWriteObject();
+            // Write out all elements in the proper order.
+            for (Node<E> p = first; p != null; p = p.next) {
+                s.writeObject(p.item);
+            }
+            // Use trailing null as sentinel
+            s.writeObject(null);
         } finally {
             lock.unlock();
         }
diff --git a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
index 71ee1b7..6d6123e 100644
--- a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
+++ b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
@@ -19,6 +19,10 @@ package org.apache.commons.pool2.impl;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.pool2.PooledObjectFactory;
 
@@ -61,7 +65,7 @@ class PoolImplUtils {
         }
         return (Class<?>) genericType;
     }
-
+    
     /**
      * Obtains the concrete type used by an implementation of an interface that uses a generic type.
      *
@@ -104,7 +108,7 @@ class PoolImplUtils {
             return null;
         }
     }
-
+    
     /**
      * Gets the matching parameterized type or null.
      * @param type
@@ -153,4 +157,43 @@ class PoolImplUtils {
         }
         return null;
     }
+
+    /**
+     * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
+     * 
+     * @param timeUnit A TimeUnit.
+     * @return The corresponding ChronoUnit.
+     */
+    static ChronoUnit toChronoUnit(TimeUnit timeUnit) {
+        // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
+        switch (Objects.requireNonNull(timeUnit)) {
+        case NANOSECONDS:
+            return ChronoUnit.NANOS;
+        case MICROSECONDS:
+            return ChronoUnit.MICROS;
+        case MILLISECONDS:
+            return ChronoUnit.MILLIS;
+        case SECONDS:
+            return ChronoUnit.SECONDS;
+        case MINUTES:
+            return ChronoUnit.MINUTES;
+        case HOURS:
+            return ChronoUnit.HOURS;
+        case DAYS:
+            return ChronoUnit.DAYS;
+        default:
+            throw new IllegalArgumentException(timeUnit.toString());
+        }
+    }
+
+    /**
+     * Converts am amount and TimeUnit into a Duration.
+     *
+     * @param amount   the amount of the duration, measured in terms of the unit, positive or negative
+     * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
+     * @return a Duration.
+     */
+    static Duration toDuration(long amount, TimeUnit timeUnit) {
+        return Duration.of(amount, PoolImplUtils.toChronoUnit(timeUnit));
+    }
 }
diff --git a/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java b/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
index 95ed195..db2c2ce 100644
--- a/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
+++ b/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.pool2;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
@@ -54,14 +55,14 @@ public final class ObjectPoolIssue326 {
         poolConfig.setLifo(true);
         poolConfig.setFairness(true);
         poolConfig.setMaxWaitMillis(30 * 1000);
-        poolConfig.setMinEvictableIdleTimeMillis(-1);
-        poolConfig.setSoftMinEvictableIdleTimeMillis(-1);
+        poolConfig.setMinEvictableIdleTime(Duration.ofMillis(-1));
+        poolConfig.setSoftMinEvictableIdleTime(Duration.ofMillis(-1));
         poolConfig.setNumTestsPerEvictionRun(1);
         poolConfig.setTestOnCreate(false);
         poolConfig.setTestOnBorrow(false);
         poolConfig.setTestOnReturn(false);
         poolConfig.setTestWhileIdle(false);
-        poolConfig.setTimeBetweenEvictionRunsMillis(5 * 1000);
+        poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(5));
         poolConfig.setEvictionPolicyClassName(BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME);
         poolConfig.setBlockWhenExhausted(false);
         poolConfig.setJmxEnabled(false);
diff --git a/src/test/java/org/apache/commons/pool2/PoolTest.java b/src/test/java/org/apache/commons/pool2/PoolTest.java
index 3fedd39..b115407 100644
--- a/src/test/java/org/apache/commons/pool2/PoolTest.java
+++ b/src/test/java/org/apache/commons/pool2/PoolTest.java
@@ -24,8 +24,11 @@ import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 
+import java.time.Duration;
+
 @Disabled
 public class PoolTest {
     private static final CharSequence COMMONS_POOL_EVICTIONS_TIMER_THREAD_NAME = "commons-pool-EvictionTimer";
@@ -72,6 +75,9 @@ public class PoolTest {
         final PooledFooFactory pooledFooFactory = new PooledFooFactory();
         try (GenericObjectPool<Foo> pool = new GenericObjectPool<>(pooledFooFactory, poolConfig)) {
             pool.setTimeBetweenEvictionRunsMillis(EVICTION_PERIOD_IN_MILLIS);
+            assertEquals(EVICTION_PERIOD_IN_MILLIS, pool.getTimeBetweenEvictionRuns().toMillis());
+            pool.setTimeBetweenEvictionRuns(Duration.ofMillis(EVICTION_PERIOD_IN_MILLIS));
+            assertEquals(EVICTION_PERIOD_IN_MILLIS, pool.getTimeBetweenEvictionRuns().toMillis());
             pool.addObject();
             try {
                 Thread.sleep(EVICTION_PERIOD_IN_MILLIS);
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
index 030f4e8..2013ca4 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
@@ -17,10 +17,15 @@
 
 package org.apache.commons.pool2.impl;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.lang.management.ManagementFactory;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.Set;
@@ -37,10 +42,6 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 /**
  * TestCase for AbandonedObjectPool
  */
@@ -199,7 +200,7 @@ public class TestAbandonedObjectPool {
         final int n = 10;
         pool.setMaxTotal(n);
         pool.setBlockWhenExhausted(false);
-        pool.setTimeBetweenEvictionRunsMillis(500);
+        pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
         PooledTestObject obj = null;
         for (int i = 0; i < 5; i++) {
             obj = pool.borrowObject();
@@ -220,7 +221,7 @@ public class TestAbandonedObjectPool {
              // validate takes 1 second
              new SimpleFactory(0, 0),
              new GenericObjectPoolConfig<PooledTestObject>(), abandonedConfig);
-        pool.setTimeBetweenEvictionRunsMillis(50);
+        pool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
         // Borrow an object, wait long enough for it to be abandoned
         final PooledTestObject obj = pool.borrowObject();
         Thread.sleep(100);
@@ -257,7 +258,7 @@ public class TestAbandonedObjectPool {
         final int n = 10;
         pool.setMaxTotal(n);
         pool.setBlockWhenExhausted(false);
-        pool.setTimeBetweenEvictionRunsMillis(500);
+        pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
         pool.setTestOnReturn(true);
         // Borrow an object, wait long enough for it to be abandoned
         // then arrange for evictor to run while it is being returned
@@ -283,7 +284,7 @@ public class TestAbandonedObjectPool {
     public void testWhenExhaustedBlock() throws Exception {
         abandonedConfig.setRemoveAbandonedOnMaintenance(true);
         pool.setAbandonedConfig(abandonedConfig);
-        pool.setTimeBetweenEvictionRunsMillis(500);
+        pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
 
         pool.setMaxTotal(1);
 
@@ -312,7 +313,7 @@ public class TestAbandonedObjectPool {
         final PrintWriter pw = new PrintWriter(bos);
         abandonedConfig.setLogWriter(pw);
         pool.setAbandonedConfig(abandonedConfig);
-        pool.setTimeBetweenEvictionRunsMillis(100);
+        pool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         final PooledTestObject o1 = pool.borrowObject();
         Thread.sleep(2000);
         assertTrue(o1.isDestroyed());
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
index a24e02f..2007e0c 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
@@ -17,6 +17,9 @@
 
 package org.apache.commons.pool2.impl;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.Duration;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.commons.pool2.impl.TestGenericObjectPool.SimpleFactory;
@@ -24,8 +27,6 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 /**
  */
 public class TestBaseGenericObjectPool {
@@ -83,10 +84,10 @@ public class TestBaseGenericObjectPool {
         final AtomicIntegerFactory factory = new AtomicIntegerFactory();
         factory.setValidateLatency(50);
         try (final GenericObjectPool<AtomicInteger> evictingPool = new GenericObjectPool<>(factory)) {
-            evictingPool.setTimeBetweenEvictionRunsMillis(100);
+            evictingPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
             evictingPool.setNumTestsPerEvictionRun(5);
             evictingPool.setTestWhileIdle(true);
-            evictingPool.setMinEvictableIdleTimeMillis(50);
+            evictingPool.setMinEvictableIdleTime(Duration.ofMillis(50));
             for (int i = 0; i < 10; i++) {
                 try {
                     evictingPool.addObject();
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestConstants.java b/src/test/java/org/apache/commons/pool2/impl/TestConstants.java
index 1eeb967..59479ad 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestConstants.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestConstants.java
@@ -23,5 +23,6 @@ public class TestConstants {
 
     public static final Duration ONE_SECOND = Duration.ofSeconds(1);
     public static final Duration ONE_MINUTE = Duration.ofMinutes(1);
+    public static final Duration ONE_MILLISECOND = Duration.ofMillis(1);
 
 }
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
index ed8faf2..74aed0e 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
@@ -18,9 +18,19 @@
 package org.apache.commons.pool2.impl;
 
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.management.ManagementFactory;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.NoSuchElementException;
@@ -55,15 +65,6 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNotSame;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
 /**
  */
 public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
@@ -388,23 +389,27 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
     public void testSettersAndGetters() throws Exception {
         {
             gkoPool.setMaxTotalPerKey(123);
-            assertEquals(123,gkoPool.getMaxTotalPerKey());
+            assertEquals(123, gkoPool.getMaxTotalPerKey());
         }
         {
             gkoPool.setMaxIdlePerKey(12);
-            assertEquals(12,gkoPool.getMaxIdlePerKey());
+            assertEquals(12, gkoPool.getMaxIdlePerKey());
         }
         {
             gkoPool.setMaxWaitMillis(1234L);
-            assertEquals(1234L,gkoPool.getMaxWaitMillis());
+            assertEquals(1234L, gkoPool.getMaxWaitMillis());
         }
         {
             gkoPool.setMinEvictableIdleTimeMillis(12345L);
-            assertEquals(12345L,gkoPool.getMinEvictableIdleTimeMillis());
+            assertEquals(12345L, gkoPool.getMinEvictableIdleTimeMillis());
+        }
+        {
+            gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L));
+            assertEquals(12345L, gkoPool.getMinEvictableIdleTime().toMillis());
         }
         {
             gkoPool.setNumTestsPerEvictionRun(11);
-            assertEquals(11,gkoPool.getNumTestsPerEvictionRun());
+            assertEquals(11, gkoPool.getNumTestsPerEvictionRun());
         }
         {
             gkoPool.setTestOnBorrow(true);
@@ -426,7 +431,11 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         }
         {
             gkoPool.setTimeBetweenEvictionRunsMillis(11235L);
-            assertEquals(11235L,gkoPool.getTimeBetweenEvictionRunsMillis());
+            assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis());
+        }
+        {
+            gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(11235L));
+            assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis());
         }
         {
             gkoPool.setBlockWhenExhausted(true);
@@ -443,8 +452,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
     @Test
     public void testContructorEvictionConfig() throws Exception {
         final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
-        config.setTimeBetweenEvictionRunsMillis(500);
-        config.setMinEvictableIdleTimeMillis(50);
+        config.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+        config.setMinEvictableIdleTime(Duration.ofMillis(50));
         config.setNumTestsPerEvictionRun(5);
         try (final GenericKeyedObjectPool<String, String> p = new GenericKeyedObjectPool<>(simpleFactory, config)) {
             for (int i = 0; i < 5; i++) {
@@ -472,8 +481,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         gkoPool.setMaxIdlePerKey(500);
         gkoPool.setMaxTotalPerKey(500);
         gkoPool.setNumTestsPerEvictionRun(100);
-        gkoPool.setMinEvictableIdleTimeMillis(250L);
-        gkoPool.setTimeBetweenEvictionRunsMillis(500L);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
 
         final String[] active = new String[500];
         for(int i=0;i<500;i++) {
@@ -523,8 +532,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         gkoPool.setMaxIdlePerKey(500);
         gkoPool.setMaxTotalPerKey(500);
         gkoPool.setNumTestsPerEvictionRun(100);
-        gkoPool.setMinEvictableIdleTimeMillis(500L);
-        gkoPool.setTimeBetweenEvictionRunsMillis(500L);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(500));
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
 
         final String[] active = new String[500];
         final String[] active2 = new String[500];
@@ -678,8 +687,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         gkoPool.setMinIdlePerKey(5);
         gkoPool.setMaxTotalPerKey(10);
         gkoPool.setNumTestsPerEvictionRun(0);
-        gkoPool.setMinEvictableIdleTimeMillis(50L);
-        gkoPool.setTimeBetweenEvictionRunsMillis(100L);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         gkoPool.setTestWhileIdle(true);
 
 
@@ -719,8 +728,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         gkoPool.setMinIdlePerKey(5);
         gkoPool.setMaxTotalPerKey(10);
         gkoPool.setNumTestsPerEvictionRun(0);
-        gkoPool.setMinEvictableIdleTimeMillis(50L);
-        gkoPool.setTimeBetweenEvictionRunsMillis(100L);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         gkoPool.setTestWhileIdle(true);
 
         final String key = "A";
@@ -773,8 +782,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         gkoPool.setMinIdlePerKey(5);
         gkoPool.setMaxTotalPerKey(10);
         gkoPool.setNumTestsPerEvictionRun(0);
-        gkoPool.setMinEvictableIdleTimeMillis(50L);
-        gkoPool.setTimeBetweenEvictionRunsMillis(100L);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         gkoPool.setTestWhileIdle(true);
 
 
@@ -846,7 +855,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         final SimpleFactory<Integer> intFactory = new SimpleFactory<>();
         try (final GenericKeyedObjectPool<Integer, String> intPool = new GenericKeyedObjectPool<>(intFactory)) {
             intPool.setNumTestsPerEvictionRun(2);
-            intPool.setMinEvictableIdleTimeMillis(100);
+            intPool.setMinEvictableIdleTime(Duration.ofMillis(100));
             intPool.setLifo(lifo);
 
             for (int i = 0; i < 3; i++) {
@@ -906,7 +915,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
             intPool.clear();
 
             // Reload
-            intPool.setMinEvictableIdleTimeMillis(500);
+            intPool.setMinEvictableIdleTime(Duration.ofMillis(500));
             intFactory.counter = 0; // Reset counter
             for (int i = 0; i < 3; i++) {
                 final Integer key = Integer.valueOf(i);
@@ -972,7 +981,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(
                 trackerFactory)) {
             intPool.setNumTestsPerEvictionRun(2);
-            intPool.setMinEvictableIdleTimeMillis(-1);
+            intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
             intPool.setTestWhileIdle(true);
             intPool.setLifo(lifo);
             intPool.setTestOnReturn(false);
@@ -1058,7 +1067,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
                     intPool.setMaxIdlePerKey(-1);
                     intPool.setMaxTotalPerKey(-1);
                     intPool.setNumTestsPerEvictionRun(smallPrimes[i]);
-                    intPool.setMinEvictableIdleTimeMillis(-1);
+                    intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
                     intPool.setTestWhileIdle(true);
                     intPool.setLifo(lifo);
                     intPool.setTestOnReturn(false);
@@ -1194,6 +1203,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
     public void testConstructors() throws Exception {
@@ -1220,8 +1230,12 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
             assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, objPool.getMaxWaitMillis());
             assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY, objPool.getMinIdlePerKey());
             assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL, objPool.getMaxTotal());
+            //
             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
                     objPool.getMinEvictableIdleTimeMillis());
+            assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
+                    objPool.getMinEvictableIdleTime());
+            //
             assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, objPool.getNumTestsPerEvictionRun());
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
                     Boolean.valueOf(objPool.getTestOnBorrow()));
@@ -1229,8 +1243,12 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
                     Boolean.valueOf(objPool.getTestOnReturn()));
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE),
                     Boolean.valueOf(objPool.getTestWhileIdle()));
+            //
             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                     objPool.getTimeBetweenEvictionRunsMillis());
+            assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
+                    objPool.getTimeBetweenEvictionRuns());
+            //
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
                     Boolean.valueOf(objPool.getBlockWhenExhausted()));
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(objPool.getLifo()));
@@ -1243,12 +1261,12 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         config.setMinIdlePerKey(minIdle);
         config.setMaxTotal(maxTotal);
         config.setMaxWaitMillis(maxWait);
-        config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        config.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
         config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
         config.setTestOnBorrow(testOnBorrow);
         config.setTestOnReturn(testOnReturn);
         config.setTestWhileIdle(testWhileIdle);
-        config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+        config.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
         config.setBlockWhenExhausted(blockWhenExhausted);
         try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory, config)) {
             assertEquals(maxTotalPerKey, objPool.getMaxTotalPerKey());
@@ -1257,11 +1275,13 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
             assertEquals(minIdle, objPool.getMinIdlePerKey());
             assertEquals(maxTotal, objPool.getMaxTotal());
             assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTimeMillis());
+            assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTime().toMillis());
             assertEquals(numTestsPerEvictionRun, objPool.getNumTestsPerEvictionRun());
             assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(objPool.getTestOnBorrow()));
             assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(objPool.getTestOnReturn()));
             assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(objPool.getTestWhileIdle()));
             assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRunsMillis());
+            assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRuns().toMillis());
             assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(objPool.getBlockWhenExhausted()));
             assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(objPool.getLifo()));
         }
@@ -2073,7 +2093,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         config.setTestOnBorrow(true);
         config.setTestOnReturn(false);
         config.setTestWhileIdle(true);
-        config.setTimeBetweenEvictionRunsMillis(-1);
+        config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
 
         try (final GenericKeyedObjectPool<Integer, Object> pool = new GenericKeyedObjectPool<>(new ObjectFactory(),
                 config)) {
@@ -2233,7 +2253,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
             pool.setTestWhileIdle(true);
             pool.setMaxTotalPerKey(2);
             pool.setNumTestsPerEvictionRun(1);
-            pool.setTimeBetweenEvictionRunsMillis(500);
+            pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
 
             // Load pool with two objects
             pool.addObject("one"); // call this o1
@@ -2391,7 +2411,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
     // POOL-326
     @Test
     public void testEvictorClearOldestRace() throws Exception {
-        gkoPool.setMinEvictableIdleTimeMillis(100);
+        gkoPool.setMinEvictableIdleTime(Duration.ofMillis(100));
         gkoPool.setNumTestsPerEvictionRun(1);
 
         // Introduce latency between when evictor starts looking at an instance and when
@@ -2408,7 +2428,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
         Thread.sleep(1000);
 
         // Start evictor and race with clearOldest
-        gkoPool.setTimeBetweenEvictionRunsMillis(10);
+        gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(10));
 
         // Wait for evictor to start
         Thread.sleep(100);
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
index 61f14ba..747faa2 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
@@ -595,29 +595,41 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
 
     private SimpleFactory simpleFactory = null;
 
-    private void assertConfiguration(final GenericObjectPoolConfig<?> expected, final GenericObjectPool<?> actual) throws Exception {
-        assertEquals(Boolean.valueOf(expected.getTestOnCreate()),
-                Boolean.valueOf(actual.getTestOnCreate()),"testOnCreate");
-        assertEquals(Boolean.valueOf(expected.getTestOnBorrow()),
-                Boolean.valueOf(actual.getTestOnBorrow()),"testOnBorrow");
-        assertEquals(Boolean.valueOf(expected.getTestOnReturn()),
-                Boolean.valueOf(actual.getTestOnReturn()),"testOnReturn");
-        assertEquals(Boolean.valueOf(expected.getTestWhileIdle()),
-                Boolean.valueOf(actual.getTestWhileIdle()),"testWhileIdle");
-        assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()),
-                Boolean.valueOf(actual.getBlockWhenExhausted()),"whenExhaustedAction");
-        assertEquals(expected.getMaxTotal(),actual.getMaxTotal(),"maxTotal");
-        assertEquals(expected.getMaxIdle(),actual.getMaxIdle(),"maxIdle");
-        assertEquals(expected.getMaxWaitMillis(),actual.getMaxWaitMillis(),"maxWait");
-        assertEquals(expected.getMinEvictableIdleTimeMillis(),actual.getMinEvictableIdleTimeMillis(),"minEvictableIdleTimeMillis");
-        assertEquals(expected.getNumTestsPerEvictionRun(),actual.getNumTestsPerEvictionRun(),"numTestsPerEvictionRun");
-        assertEquals(expected.getEvictorShutdownTimeoutMillis(),actual.getEvictorShutdownTimeoutMillis(),"evictorShutdownTimeoutMillis");
-        assertEquals(expected.getTimeBetweenEvictionRunsMillis(),actual.getTimeBetweenEvictionRunsMillis(),"timeBetweenEvictionRunsMillis");
+    @SuppressWarnings("deprecation")
+    private void assertConfiguration(final GenericObjectPoolConfig<?> expected, final GenericObjectPool<?> actual)
+            throws Exception {
+        assertEquals(Boolean.valueOf(expected.getTestOnCreate()), Boolean.valueOf(actual.getTestOnCreate()),
+                "testOnCreate");
+        assertEquals(Boolean.valueOf(expected.getTestOnBorrow()), Boolean.valueOf(actual.getTestOnBorrow()),
+                "testOnBorrow");
+        assertEquals(Boolean.valueOf(expected.getTestOnReturn()), Boolean.valueOf(actual.getTestOnReturn()),
+                "testOnReturn");
+        assertEquals(Boolean.valueOf(expected.getTestWhileIdle()), Boolean.valueOf(actual.getTestWhileIdle()),
+                "testWhileIdle");
+        assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()), Boolean.valueOf(actual.getBlockWhenExhausted()),
+                "whenExhaustedAction");
+        assertEquals(expected.getMaxTotal(), actual.getMaxTotal(), "maxTotal");
+        assertEquals(expected.getMaxIdle(), actual.getMaxIdle(), "maxIdle");
+        assertEquals(expected.getMaxWaitMillis(), actual.getMaxWaitMillis(), "maxWait");
+        assertEquals(expected.getMinEvictableIdleTimeMillis(), actual.getMinEvictableIdleTimeMillis(),
+                "minEvictableIdleTimeMillis");
+        assertEquals(expected.getMinEvictableIdleTime(), actual.getMinEvictableIdleTime(),
+                "minEvictableIdleTime");
+        assertEquals(expected.getNumTestsPerEvictionRun(), actual.getNumTestsPerEvictionRun(),
+                "numTestsPerEvictionRun");
+        assertEquals(expected.getEvictorShutdownTimeoutMillis(), actual.getEvictorShutdownTimeoutMillis(),
+                "evictorShutdownTimeoutMillis");
+        assertEquals(expected.getEvictorShutdownTimeout(), actual.getEvictorShutdownTimeout(),
+                "evictorShutdownTimeout");
+        assertEquals(expected.getTimeBetweenEvictionRunsMillis(), actual.getTimeBetweenEvictionRunsMillis(),
+                "timeBetweenEvictionRunsMillis");
+        assertEquals(expected.getTimeBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(),
+                "timeBetweenEvictionRuns");
     }
 
     private void checkEvict(final boolean lifo) throws Exception {
         // yea this is hairy but it tests all the code paths in GOP.evict()
-        genericObjectPool.setSoftMinEvictableIdleTimeMillis(10);
+        genericObjectPool.setSoftMinEvictableIdleTime(Duration.ofMillis(10));
         genericObjectPool.setMinIdle(2);
         genericObjectPool.setTestWhileIdle(true);
         genericObjectPool.setLifo(lifo);
@@ -648,7 +660,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
 
     private void checkEvictionOrderPart1(final boolean lifo) throws Exception {
         genericObjectPool.setNumTestsPerEvictionRun(2);
-        genericObjectPool.setMinEvictableIdleTimeMillis(100);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
         genericObjectPool.setLifo(lifo);
         for (int i = 0; i < 5; i++) {
             genericObjectPool.addObject();
@@ -666,7 +678,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
     private void checkEvictionOrderPart2(final boolean lifo) throws Exception {
         // Two eviction runs in sequence
         genericObjectPool.setNumTestsPerEvictionRun(2);
-        genericObjectPool.setMinEvictableIdleTimeMillis(100);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
         genericObjectPool.setLifo(lifo);
         for (int i = 0; i < 5; i++) {
             genericObjectPool.addObject();
@@ -683,7 +695,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>();
         try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
             trackerPool.setNumTestsPerEvictionRun(2);
-            trackerPool.setMinEvictableIdleTimeMillis(-1);
+            trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
             trackerPool.setTestWhileIdle(true);
             trackerPool.setLifo(lifo);
             trackerPool.setTestOnReturn(false);
@@ -714,7 +726,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         trackerFactory = new VisitTrackerFactory<>();
         try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
             trackerPool.setNumTestsPerEvictionRun(3);
-            trackerPool.setMinEvictableIdleTimeMillis(-1);
+            trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
             trackerPool.setTestWhileIdle(true);
             trackerPool.setLifo(lifo);
             trackerPool.setTestOnReturn(false);
@@ -757,7 +769,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             for (int j = 0; j < 5; j++) {
                 try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) {
                     trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]);
-                    trackerPool.setMinEvictableIdleTimeMillis(-1);
+                    trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
                     trackerPool.setTestWhileIdle(true);
                     trackerPool.setLifo(lifo);
                     trackerPool.setTestOnReturn(false);
@@ -1090,8 +1102,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
     @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
     public void testCloseMultiplePools1() throws Exception {
         try (final GenericObjectPool<String> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
-            genericObjectPool.setTimeBetweenEvictionRunsMillis(1);
-            genericObjectPool2.setTimeBetweenEvictionRunsMillis(1);
+            genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND);
+            genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND);
         }
         genericObjectPool.close();
     }
@@ -1103,10 +1115,10 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty
             simpleFactory.setDestroyLatency(1000L);
             // Ensure there is an object to evict, so that above latency takes effect
-            genericObjectPool.setTimeBetweenEvictionRunsMillis(1);
-            genericObjectPool2.setTimeBetweenEvictionRunsMillis(1);
-            genericObjectPool.setMinEvictableIdleTimeMillis(1);
-            genericObjectPool2.setMinEvictableIdleTimeMillis(1);
+            genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND);
+            genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND);
+            genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND);
+            genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND);
             genericObjectPool.addObject();
             genericObjectPool2.addObject();
             // Close both pools
@@ -1228,6 +1240,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal());
             assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
                     dummyPool.getMinEvictableIdleTimeMillis());
+            assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
+                    dummyPool.getMinEvictableIdleTime());
             assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                     dummyPool.getNumTestsPerEvictionRun());
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
@@ -1238,6 +1252,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
                     Boolean.valueOf(dummyPool.getTestWhileIdle()));
             assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
                     dummyPool.getTimeBetweenEvictionRunsMillis());
+            assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
+                    dummyPool.getTimeBetweenEvictionRuns());
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
                     Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
             assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo()));
@@ -1250,11 +1266,13 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         config.setMaxTotal(maxTotal);
         config.setMaxWaitMillis(maxWait);
         config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        assertEquals(minEvictableIdleTimeMillis, config.getMinEvictableIdleTime().toMillis());
         config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
         config.setTestOnBorrow(testOnBorrow);
         config.setTestOnReturn(testOnReturn);
         config.setTestWhileIdle(testWhileIdle);
         config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+        assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis());
         config.setBlockWhenExhausted(blockWhenExhausted);
         try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory, config)) {
             assertEquals(maxIdle, dummyPool.getMaxIdle());
@@ -1364,7 +1382,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         final TestThread<String> borrower = new TestThread<>(genericObjectPool, 1, 150, false);
         final Thread borrowerThread = new Thread(borrower);
         // Set evictor to run in 100 ms - will create idle instance
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(100);
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         borrowerThread.start();  // Off to the races
         borrowerThread.join();
         assertTrue(!borrower.failed());
@@ -1382,8 +1400,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMaxIdle(500);
         genericObjectPool.setMaxTotal(500);
         genericObjectPool.setNumTestsPerEvictionRun(100);
-        genericObjectPool.setMinEvictableIdleTimeMillis(250L);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(500L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
         genericObjectPool.setTestWhileIdle(true);
 
         final String[] active = new String[500];
@@ -1439,7 +1457,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             invalidFactoryPool.setTestOnBorrow(false);
             invalidFactoryPool.setTestOnReturn(false);
             invalidFactoryPool.setTestWhileIdle(true);
-            invalidFactoryPool.setMinEvictableIdleTimeMillis(100000);
+            invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100));
             invalidFactoryPool.setNumTestsPerEvictionRun(1);
 
             final Object p = invalidFactoryPool.borrowObject();
@@ -1490,8 +1508,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMaxIdle(500);
         genericObjectPool.setMaxTotal(500);
         genericObjectPool.setNumTestsPerEvictionRun(500);
-        genericObjectPool.setMinEvictableIdleTimeMillis(250L);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(500L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
         genericObjectPool.setTestWhileIdle(true);
 
         // ClassNotFoundException
@@ -1581,8 +1599,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             timePool.setMaxIdle(5);
             timePool.setMaxTotal(5);
             timePool.setNumTestsPerEvictionRun(5);
-            timePool.setMinEvictableIdleTimeMillis(3000L);
-            timePool.setSoftMinEvictableIdleTimeMillis(1000L);
+            timePool.setMinEvictableIdleTime(Duration.ofSeconds(3));
+            timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND);
             timePool.setMinIdle(2);
 
             final TimeTest[] active = new TimeTest[5];
@@ -1615,8 +1633,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMaxIdle(6);
         genericObjectPool.setMaxTotal(6);
         genericObjectPool.setNumTestsPerEvictionRun(-2);
-        genericObjectPool.setMinEvictableIdleTimeMillis(50L);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(100L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
 
         final String[] active = new String[6];
         for (int i = 0; i < 6; i++) {
@@ -1997,7 +2015,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         simpleFactory.setMaxTotal(maxTotal);
         genericObjectPool.setMaxTotal(maxTotal);
         genericObjectPool.setBlockWhenExhausted(true);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(-1);
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
 
         // this is important to trigger POOL-356
         genericObjectPool.setMaxIdle(0);
@@ -2141,7 +2159,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         simpleFactory.setMaxTotal(maxTotal);
         genericObjectPool.setMaxTotal(maxTotal);
         genericObjectPool.setBlockWhenExhausted(true);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(-1);
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
 
         // Start threads to borrow objects
         final TestThread[] threads = new TestThread[numThreads];
@@ -2283,8 +2301,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMinIdle(5);
         genericObjectPool.setMaxTotal(10);
         genericObjectPool.setNumTestsPerEvictionRun(0);
-        genericObjectPool.setMinEvictableIdleTimeMillis(50L);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(100L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         genericObjectPool.setTestWhileIdle(true);
 
         try { Thread.sleep(150L); } catch(final InterruptedException e) { }
@@ -2318,8 +2336,8 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMinIdle(5);
         genericObjectPool.setMaxTotal(10);
         genericObjectPool.setNumTestsPerEvictionRun(0);
-        genericObjectPool.setMinEvictableIdleTimeMillis(50L);
-        genericObjectPool.setTimeBetweenEvictionRunsMillis(100L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+        genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
         genericObjectPool.setTestWhileIdle(true);
 
         try { Thread.sleep(150L); } catch(final InterruptedException e) { }
@@ -2552,13 +2570,13 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         expected.setMaxTotal(2);
         expected.setMaxIdle(3);
         expected.setMaxWaitMillis(5L);
-        expected.setMinEvictableIdleTimeMillis(7L);
+        expected.setMinEvictableIdleTime(Duration.ofMillis(7L));
         expected.setNumTestsPerEvictionRun(9);
         expected.setTestOnCreate(true);
         expected.setTestOnBorrow(true);
         expected.setTestOnReturn(true);
         expected.setTestWhileIdle(true);
-        expected.setTimeBetweenEvictionRunsMillis(11L);
+        expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L));
         expected.setBlockWhenExhausted(false);
         genericObjectPool.setConfig(expected);
         assertConfiguration(expected,genericObjectPool);
@@ -2620,6 +2638,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         {
             genericObjectPool.setMinEvictableIdleTimeMillis(12345L);
             assertEquals(12345L,genericObjectPool.getMinEvictableIdleTimeMillis());
+            assertEquals(12345L,genericObjectPool.getMinEvictableIdleTime().toMillis());
         }
         {
             genericObjectPool.setNumTestsPerEvictionRun(11);
@@ -2646,10 +2665,12 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         {
             genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L);
             assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRunsMillis());
+            assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRuns().toMillis());
         }
         {
             genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L);
             assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTimeMillis());
+            assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTime().toMillis());
         }
         {
             genericObjectPool.setBlockWhenExhausted(true);
@@ -2666,7 +2687,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         genericObjectPool.setMaxIdle(6);
         genericObjectPool.setMaxTotal(6);
         genericObjectPool.setNumTestsPerEvictionRun(6);
-        genericObjectPool.setMinEvictableIdleTimeMillis(100L);
+        genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
 
         for (int j = 0; j < 2; j++) {
             // populate the pool
@@ -2684,7 +2705,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
             assertEquals(6,genericObjectPool.getNumIdle(),"Should have 6 idle");
 
             // start the evictor
-            genericObjectPool.setTimeBetweenEvictionRunsMillis(50L);
+            genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
 
             // wait a second (well, .2 seconds)
             try { Thread.sleep(200L); } catch(final InterruptedException e) { }
@@ -2968,7 +2989,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
         final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
         config.setJmxEnabled(false);
         GenericObjectPool<String> abandoned = new GenericObjectPool<>(simpleFactory, config);
-        abandoned.setTimeBetweenEvictionRunsMillis(100); // Starts evictor
+        abandoned.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
         assertEquals(abandoned.getRemoveAbandonedTimeout(), abandoned.getRemoveAbandonedTimeoutDuration().getSeconds());
 
         // This is ugly, but forces gc to hit the pool
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
index dd9bf22..b00d8d9 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
@@ -18,6 +18,7 @@ package org.apache.commons.pool2.impl;
 
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.time.Duration;
 
 import org.apache.commons.pool2.BasePooledObjectFactory;
 import org.apache.commons.pool2.PooledObject;
@@ -45,7 +46,7 @@ public class TestGenericObjectPoolClassLoaders {
                     new CustomClassLoaderObjectFactory(1);
             final GenericObjectPool<URL> pool1 = new GenericObjectPool<>(factory1);
             pool1.setMinIdle(1);
-            pool1.setTimeBetweenEvictionRunsMillis(100);
+            pool1.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
             int counter = 0;
             while (counter < 50 && pool1.getNumIdle() != 1) {
                 Thread.sleep(100);
@@ -66,7 +67,7 @@ public class TestGenericObjectPoolClassLoaders {
             assertEquals( 1, pool2.getNumIdle(),"Wrong number of idle objects in pool2");
             pool2.clear();
 
-            pool2.setTimeBetweenEvictionRunsMillis(100);
+            pool2.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
 
             counter = 0;
             while (counter < 50 && pool2.getNumIdle() != 1) {
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolFactoryCreateFailure.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolFactoryCreateFailure.java
index eec7725..3d85b87 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolFactoryCreateFailure.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolFactoryCreateFailure.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.pool2.impl;
 
+import java.time.Duration;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -111,9 +112,9 @@ public class TestGenericObjectPoolFactoryCreateFailure {
         config.setTestOnBorrow(true);
         config.setTestOnReturn(true);
         config.setTestWhileIdle(false);
-        config.setTimeBetweenEvictionRunsMillis(-1);
-        config.setMinEvictableIdleTimeMillis(-1);
-        config.setSoftMinEvictableIdleTimeMillis(-1);
+        config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+        config.setMinEvictableIdleTime(Duration.ofMillis(-1));
+        config.setSoftMinEvictableIdleTime(Duration.ofMillis(-1));
 
         config.setMaxWaitMillis(-1);
         try (GenericObjectPool<Object> pool = new GenericObjectPool<>(factory, config)) {
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java b/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
index 3c0e236..edcb360 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
@@ -21,6 +21,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -40,6 +41,7 @@ import static org.junit.jupiter.api.Assertions.fail;
  */
 public class TestLinkedBlockingDeque {
 
+    private static final Duration TIMEOUT_50_MILLIS = Duration.ofMillis(50);
     private static final Integer ONE = Integer.valueOf(1);
     private static final Integer TWO = Integer.valueOf(2);
     private static final Integer THREE = Integer.valueOf(3);
@@ -148,9 +150,9 @@ public class TestLinkedBlockingDeque {
             deque.offerFirst(null);
             fail("Not supposed to get here");
         } catch (final NullPointerException e) {}
-        assertTrue(deque.offerFirst(ONE, 50, TimeUnit.MILLISECONDS));
-        assertTrue(deque.offerFirst(TWO, 50, TimeUnit.MILLISECONDS));
-        assertFalse(deque.offerFirst(THREE, 50, TimeUnit.MILLISECONDS));
+        assertTrue(deque.offerFirst(ONE, TIMEOUT_50_MILLIS));
+        assertTrue(deque.offerFirst(TWO, TIMEOUT_50_MILLIS));
+        assertFalse(deque.offerFirst(THREE, TIMEOUT_50_MILLIS));
     }
 
     @Test
@@ -159,9 +161,9 @@ public class TestLinkedBlockingDeque {
             deque.offerLast(null);
             fail("Not supposed to get here");
         } catch (final NullPointerException e) {}
-        assertTrue(deque.offerLast(ONE, 50, TimeUnit.MILLISECONDS));
-        assertTrue(deque.offerLast(TWO, 50, TimeUnit.MILLISECONDS));
-        assertFalse(deque.offerLast(THREE, 50, TimeUnit.MILLISECONDS));
+        assertTrue(deque.offerLast(ONE, TIMEOUT_50_MILLIS));
+        assertTrue(deque.offerLast(TWO, TIMEOUT_50_MILLIS));
+        assertFalse(deque.offerLast(THREE, TIMEOUT_50_MILLIS));
     }
 
     @Test
@@ -229,13 +231,13 @@ public class TestLinkedBlockingDeque {
     @Test
     public void testPollFirstWithTimeout() throws InterruptedException {
         assertNull(deque.pollFirst());
-        assertNull(deque.pollFirst(50, TimeUnit.MILLISECONDS));
+        assertNull(deque.pollFirst(TIMEOUT_50_MILLIS));
     }
 
     @Test
     public void testPollLastWithTimeout() throws InterruptedException {
         assertNull(deque.pollLast());
-        assertNull(deque.pollLast(50, TimeUnit.MILLISECONDS));
+        assertNull(deque.pollLast(TIMEOUT_50_MILLIS));
     }
 
     @Test
@@ -323,11 +325,11 @@ public class TestLinkedBlockingDeque {
 
     @Test
     public void testOfferWithTimeout() throws InterruptedException {
-        assertTrue(deque.offer(ONE, 50, TimeUnit.MILLISECONDS));
-        assertTrue(deque.offer(TWO, 50, TimeUnit.MILLISECONDS));
-        assertFalse(deque.offer(THREE, 50, TimeUnit.MILLISECONDS));
+        assertTrue(deque.offer(ONE, TIMEOUT_50_MILLIS));
+        assertTrue(deque.offer(TWO, TIMEOUT_50_MILLIS));
+        assertFalse(deque.offer(THREE, TIMEOUT_50_MILLIS));
         try {
-            deque.offer(null, 50, TimeUnit.MILLISECONDS);
+            deque.offer(null, TIMEOUT_50_MILLIS);
             fail("Not supposed to get here");
         } catch (final NullPointerException e) {}
     }
@@ -352,8 +354,8 @@ public class TestLinkedBlockingDeque {
 
     @Test
     public void testPollWithTimeout() throws InterruptedException {
-        assertNull(deque.poll(50, TimeUnit.MILLISECONDS));
-        assertNull(deque.poll(50, TimeUnit.MILLISECONDS));
+        assertNull(deque.poll(TIMEOUT_50_MILLIS));
+        assertNull(deque.poll(TIMEOUT_50_MILLIS));
     }
 
     @Test