Hi everyone,

Here is one PR #1183 <https://github.com/h2database/h2database/pull/1183>.

JdbcConnectionPool comes with an object level lock for almost every 
operation, this may be a perforamce issue in multi-thread program.

A wait-free implement with atomic types and wait-free queue may help.


I wrote the code, it's mine, and I'm contributing it to H2 for distribution 
multiple-licensed under the MPL 2.0, and the EPL 1.0 
(http://h2database.com/html/license.html).

-- 
You received this message because you are subscribed to the Google Groups "H2 
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
Index: h2/src/main/org/h2/jdbcx/JdbcConnectionPool.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/jdbcx/JdbcConnectionPool.java	(revision 0c46e47e7824f4fd366f0d7cebd91f6e45f106ed)
+++ h2/src/main/org/h2/jdbcx/JdbcConnectionPool.java	(revision 1107e4e59a102edc6251682af458d28b8b8f8585)
@@ -22,8 +22,11 @@
 import java.io.PrintWriter;
 import java.sql.Connection;
 import java.sql.SQLException;
-import java.util.ArrayList;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Logger;
 
 import javax.sql.ConnectionEvent;
@@ -33,7 +36,6 @@
 import javax.sql.PooledConnection;
 
 import org.h2.message.DbException;
-import org.h2.util.Utils;
 
 /**
  * A simple standalone JDBC connection pool.
@@ -69,12 +71,12 @@
     private static final int DEFAULT_MAX_CONNECTIONS = 10;
 
     private final ConnectionPoolDataSource dataSource;
-    private final ArrayList<PooledConnection> recycledConnections = Utils.newSmallArrayList();
+    private final Queue<PooledConnection> recycledConnections = new ConcurrentLinkedQueue<>();
     private PrintWriter logWriter;
-    private int maxConnections = DEFAULT_MAX_CONNECTIONS;
-    private int timeout = DEFAULT_TIMEOUT;
-    private int activeConnections;
-    private boolean isDisposed;
+    private volatile int maxConnections = DEFAULT_MAX_CONNECTIONS;
+    private volatile int timeout = DEFAULT_TIMEOUT;
+    private AtomicInteger activeConnections = new AtomicInteger(0);
+    private AtomicBoolean isDisposed = new AtomicBoolean(false);
 
     protected JdbcConnectionPool(ConnectionPoolDataSource dataSource) {
         this.dataSource = dataSource;
@@ -120,13 +122,11 @@
      *
      * @param max the maximum number of connections
      */
-    public synchronized void setMaxConnections(int max) {
+    public void setMaxConnections(int max) {
         if (max < 1) {
             throw new IllegalArgumentException("Invalid maxConnections value: " + max);
         }
         this.maxConnections = max;
-        // notify waiting threads if the value was increased
-        notifyAll();
     }
 
     /**
@@ -134,7 +134,7 @@
      *
      * @return the max the maximum number of connections
      */
-    public synchronized int getMaxConnections() {
+    public int getMaxConnections() {
         return maxConnections;
     }
 
@@ -144,7 +144,7 @@
      * @return the timeout in seconds
      */
     @Override
-    public synchronized int getLoginTimeout() {
+    public int getLoginTimeout() {
         return timeout;
     }
 
@@ -156,7 +156,7 @@
      * @param seconds the timeout, 0 meaning the default
      */
     @Override
-    public synchronized void setLoginTimeout(int seconds) {
+    public void setLoginTimeout(int seconds) {
         if (seconds == 0) {
             seconds = DEFAULT_TIMEOUT;
         }
@@ -167,13 +167,12 @@
      * Closes all unused pooled connections.
      * Exceptions while closing are written to the log stream (if set).
      */
-    public synchronized void dispose() {
-        if (isDisposed) {
-            return;
-        }
-        isDisposed = true;
-        for (PooledConnection aList : recycledConnections) {
-            closeConnection(aList);
+    public void dispose() {
+        isDisposed.set(true);
+
+        PooledConnection pc;
+        while ((pc = recycledConnections.poll()) != null) {
+            closeConnection(pc);
         }
     }
 
@@ -193,18 +192,28 @@
     @Override
     public Connection getConnection() throws SQLException {
         long max = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeout);
+        int spin = 0;
         do {
-            synchronized (this) {
-                if (activeConnections < maxConnections) {
+            if (activeConnections.incrementAndGet() <= maxConnections) {
+                try {
                     return getConnectionNow();
+                } catch (Throwable t) {
+                    activeConnections.decrementAndGet();
+                    throw t;
                 }
-                try {
-                    wait(1000);
-                } catch (InterruptedException e) {
-                    // ignore
-                }
+            } else {
+                activeConnections.decrementAndGet();
+            }
+            if (--spin >= 0) {
+                continue;
+            }
+            try {
+                spin = 3;
+                Thread.sleep(1);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
             }
-        } while (System.nanoTime() <= max);
+        } while (System.nanoTime() - max <= 0);
         throw new SQLException("Login timeout", "08001", 8001);
     }
 
@@ -217,17 +226,14 @@
     }
 
     private Connection getConnectionNow() throws SQLException {
-        if (isDisposed) {
+        if (isDisposed.get()) {
             throw new IllegalStateException("Connection pool has been disposed.");
         }
-        PooledConnection pc;
-        if (!recycledConnections.isEmpty()) {
-            pc = recycledConnections.remove(recycledConnections.size() - 1);
-        } else {
+        PooledConnection pc = recycledConnections.poll();
+        if (pc == null) {
             pc = dataSource.getPooledConnection();
         }
         Connection conn = pc.getConnection();
-        activeConnections++;
         pc.addConnectionEventListener(this);
         return conn;
     }
@@ -239,19 +245,20 @@
      *
      * @param pc the pooled connection
      */
-    synchronized void recycleConnection(PooledConnection pc) {
-        if (activeConnections <= 0) {
+    private void recycleConnection(PooledConnection pc) {
+        int active = activeConnections.decrementAndGet();
+        if (active < 0) {
+            activeConnections.incrementAndGet();
             throw new AssertionError();
         }
-        activeConnections--;
-        if (!isDisposed && activeConnections < maxConnections) {
+        if (!isDisposed.get() && active < maxConnections) {
             recycledConnections.add(pc);
+            if (isDisposed.get()) {
+                dispose();
+            }
         } else {
             closeConnection(pc);
         }
-        if (activeConnections >= maxConnections - 1) {
-            notifyAll();
-        }
     }
 
     private void closeConnection(PooledConnection pc) {
@@ -290,8 +297,8 @@
      *
      * @return the number of active connections.
      */
-    public synchronized int getActiveConnections() {
-        return activeConnections;
+    public int getActiveConnections() {
+        return activeConnections.get();
     }
 
     /**

Reply via email to