八、Java高级特性(wait,notify/notifyAll)等待超时模式实现一个连接池

一、前言

今天我们通过wait,notify/notifyAll的知识实现一个等待超时的数据库连接池

二、代码

1、自定义一个数据库连接类

package com.it.test.thread;

public class MyConnction {
    public void createStatement() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void commit() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

2、创建连接池

package com.it.test.thread;

import java.util.LinkedList;

public class ConnectionPool {
    /**
     * 数据库连接池
     */
    LinkedList<MyConnction> pools = new LinkedList<>();
    /**
     * 默认1S的超时时间
     */
    private static final int TIME = 100;


    /**
     * 初始化连接池的
     *
     * @param size
     */
    public ConnectionPool(int size) {
        for (int i = 0; i < size; i++) {
            pools.addLast(new MyConnction());
        }
    }

    /**
     * 从连接池中获取连接
     *
     * @return
     */
    public MyConnction getPool() {
        synchronized (pools) {
            long outTime = System.currentTimeMillis() + TIME;
            /**
             * 如果连接是空的,并且超时时间未结束,则等待休眠
             */
            while (pools.isEmpty() && (outTime - System.currentTimeMillis()) > 0) {
                try {
                    //每次被唤醒之后(有可能被其他线程唤醒)
                    //根据计算剩余的超时时间继续休眠
                    pools.wait(outTime - System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (!pools.isEmpty()) {
                return pools.removeFirst();
            } else {
                return null;
            }


        }


    }

    /**
     * 释放连接
     */
    public void releasePool(MyConnction connection) {
        if (connection != null) {
            synchronized (pools) {
                pools.addLast(connection);
                pools.notifyAll();

            }

        }


    }


}

3、编写测试类

package com.it.test.thread.pool;


import com.it.test.thread.ConnectionPool;
import com.it.test.thread.MyConnction;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *类说明:
 */
public class DBPoolTest {
    static ConnectionPool pool  = new ConnectionPool(10);
    // 控制器:控制main线程将会等待所有Woker结束后才能继续执行
    static CountDownLatch end;

    public static void main(String[] args) throws Exception {
        // 线程数量
        int threadCount = 50;
        end = new CountDownLatch(threadCount);
        int count = 20;//每个线程的操作次数
        AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
        AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(new Worker(count, got, notGot), 
                    "worker_"+i);
            thread.start();
        }
        end.await();// main线程在此处等待
        System.out.println("总共尝试了: " + (threadCount * count));
        System.out.println("拿到连接的次数:  " + got);
        System.out.println("没能连接的次数: " + 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 {
                    // 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
                    // 分别统计连接获取的数量got和未获取到的数量notGot
                    MyConnction connection = pool.getPool();
                    if (connection != null)
                    {
                        try {
                            connection.createStatement();
//                            PreparedStatement preparedStatement
//                                    = connection.prepareStatement("");
//                            preparedStatement.execute();
                            connection.commit();
                        } finally {
                            pool.releasePool(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                                +"等待超时!");
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}

总共尝试了: 1000
拿到连接的次数:  652
没能连接的次数: 348

总结

wait,notify/notifyAll属于Object下的方法,使用的时候必须在同步代码块内,也就是必须在synchronized 关键字修饰的代码内。

  • 调用wait方法的时候,会释放当前的锁,进入等待状态
  • 调用notify和notifyAll方法,会随机唤醒或者全部唤醒一个线程,尽量使用notifyAll。因为notifyAll唤醒的是全部线程。当之前进入wait状态的线程收到notify唤醒通知之后,重新竞争锁,如果竞争到了,那么就在调用wait方法的地方继续往下执行。
    -调用wait方法会释放当前锁,而调用notify和notifyAll不会释放当前锁,只有执行完notify部分的代码完,才会释放锁,所以通常我们把调用notify方法方在最后。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章