java Concurrent Programming: using object waiting and notification mechanism to implement a connection pool with waiting timeout

Keywords: Programming Database Java JDBC

As we all know, Java Object objects provide interfaces such as wait() and notify()/notifyAll(), which are important parts of concurrent programming. They play a very important role in the cooperation between multi threads, and there are many scenarios that can be used in the actual development. Needless to say, today we will use this mechanism to simulate the implementation of a jdbc connection pool that supports the wait timeout mode.


1, Simulate and implement a database connection interface

//Class description: empty implementation of a Connection interface (because the focus is not here, the methods in the following interface only do simple processing)
public class SqlConnectImpl implements Connection{
   
   /*Take a database connection*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }

   @Override
   public boolean isWrapperFor(Class<?> arg0) throws SQLException {
      // TODO Auto-generated method stub
      return false;
   }
   //Because the point is not here, other interfaces are omitted here
 }


2, The core method to realize the connection pool of database waiting timeout


//Class description: implementation of connection pool
DBPool {
    //Simulation: database connection pool
    LinkedList<Connection> pool = LinkedList<Connection>()(initialSize) {
        if(initialSize > ) {
            for(int i = 0;i < initialSize; i++) {
                pool.addLast(SqlConnectImpl.fetchConnection());
            }
        }
    }

    //Connection pool: release connection, notify other threads
    public void releaseConnection(Connection connection) {
        if (connection != null) {
            synchronized (pool){
                pool.addLast(connection);
                pool.notifyAll();
            }
        }
    }

    //Connection pool: get connection usage
    public Connection fetchConnection(long mills) throws InterruptedException {
        synchronized (pool){
            //Timeout not set, get directly
            if(mills <0){
                while (pool.isEmpty()){
                    pool.wait();
                }
                return pool.removeFirst();
            }
            //Set timeout
            long future = System.currentTimeMillis()+mills;/*Timeout*/
            long remaining = mills;
            while (pool.isEmpty() && remaining > 0){
                pool.wait(remaining);
                //Wake up once: recalculate the waiting time
                remaining = future - System.currentTimeMillis();
            }
            Connection connection = null;
            if(!pool.isEmpty()){
                connection = pool.removeFirst();
            }
            return connection;
        }
    }
}


3, Access to connection pool in multithreaded concurrent mode


//Class description: database connection pool test class
public class DBPoolTest {
    static DBPool pool  = new DBPool(10);
    //Controller: control the main thread and wait for all wokers to finish before execution
    static CountDownLatch end;

    public static void main(String[] args) throws Exception {
       //Number of threads
        int threadCount = 50;
        end = new CountDownLatch(threadCount);
        int count = 20;//Number of operations per thread
        AtomicInteger got = new AtomicInteger();//Counter: count the threads that can get the connection
        AtomicInteger notGot = new AtomicInteger();//Counter: count the threads that did not get the connection
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(new Worker(count, got, notGot), 
                  "worker_"+i);
            thread.start();
        }
        end.await();//main thread is waiting here
        System.out.println("All in all: " + (threadCount * count));
        System.out.println("Number of connections received:  " + got);
        System.out.println("Number of failed connections: " + notGot);
    }

    static class Worker implements Runnable {
        int           count;
        AtomicInteger got;
        AtomicInteger notGot;

        public Worker(int count, AtomicInteger got,
                               AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        public void run() {
            while (count > 0) {
                try {
                    //Get the connection from the thread pool. If the connection cannot be obtained within 1000ms, null will be returned
                    //Count the number of got connections and the number of not got connections respectively
                    Connection connection = pool.fetchConnection(1000);
                    if (connection != null) {
                        try {
                            connection.createStatement();
                            connection.commit();
                        } finally {
                            pool.releaseConnection(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                              +"Wait timeout!");
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}


4, Test result report


5, Conclusion

Conclusion: 1) it is necessary to use wait(), notify(), notifyAll() and other methods. synchronized key packages (objects, methods or blocks) can be used, and errors must be reported during package operation;

2) when the thread executes the wait() method, it will automatically release the held lock;

3) after a thread executes notify() or notifyAll(), the lock resources held by the thread will not be released immediately, and will only be released after the statement block or method of the synchronized package is executed;

4) when using notify() method to wake up, only one thread will wake up randomly, which is not applicable under the condition of multiple wakeups. It is recommended that notifyAll() be used to wake up all threads related to lock objects;



Posted by ZaphodQB on Sun, 17 May 2020 07:29:21 -0700