wait、notify实战之实现链接池

前言

在了解到wait、notify的使用方式以后,咱们使用wait、notify来实现一个链接池。若是还有不清楚wait、notify使用的,请进入传送门:http://www.javashuo.com/article/p-rokuvpbq-cw.htmljava

链接池原理

首先咱们先来了解下链接池的基本原理
图片描述segmentfault

  • 线程调用链接池的方法来获取链接
  • 链接池内部判断链接池是否足够,若是足够则直接返回一个链接给线程
  • 链接不够的状况下,判断链接池中是否容许新建链接(好比链接数小于最大链接数)。若是容许,则建立一个新的链接并返回给线程。
  • 若是不容许新建链接,则判断是否容许等待空闲的链接,若是不容许,则未拿到链接
  • 若是容许等待,以后就判断等待是否超时。若是超时,也是未拿到链接,若是未超时,则继续等待,一直循环。

代码实现

首先咱们有一个空实现的链接类Connection函数

@Data
static class Connection {
    private String connectionName;
    public Connection(String connectionName) {
        this.connectionName = connectionName;
    }
}

此处只为了测试,该类只有一个名字属性。另外使用了lombok来自动生成set、get方法
接下来就是链接池的基本实现测试

@Data
public class ConnectionPoolOfWaitNotify {

    private Integer capacity = 4;//链接池中的链接数
    LinkedList<Connection> linkedList = new LinkedList<>(); //链接容器

    public ConnectionPoolOfWaitNotify() {
        IntStream.rangeClosed(1, capacity).forEach(i -> {//构造方法中初始化全部链接
            linkedList.addLast(new Connection("connection-" + i));
        });
    }

    //获取链接
    public Connection getConnectioin(long time) throws InterruptedException {
        synchronized (linkedList) {
            if (!linkedList.isEmpty()) {//若是存在,拿走第一个
                return linkedList.removeFirst();
            }

            if (time <= 0) {//等到拿到链接再返回
                while (linkedList.isEmpty()) {
                    linkedList.wait();
                }
                return linkedList.removeFirst();
            }

            long lastTime = System.currentTimeMillis() + time;
            long sleepTime = time;
            while (linkedList.isEmpty() && sleepTime > 0) {
                linkedList.wait(sleepTime);
                sleepTime = lastTime - System.currentTimeMillis();
            }

            if (!linkedList.isEmpty()) {
                return linkedList.removeFirst();
            } else {
                return null;
            }
        }
    }

    //归还链接
    public void revertConnection(Connection connection) {
        synchronized (linkedList) {
            linkedList.addLast(connection);
            linkedList.notifyAll();//归还链接后通知其余拿链接的线程
        }
    }
}

链接池中,主要有两个方法。一个是获取链接(可控制超时时间,超时时间小于等于0则永不超时),一个是归还链接。
主要的逻辑在获取链接的方法里面,当获取不到链接时,使用wait()方法来使得当前获取链接的线程进入等待状态。而后在归还链接成功以后,调用notifyAll()方法通知全部正在等待的线程能够继续获取链接了,可是继续获取链接时,还必须继续抢夺锁,只有占锁成功的线程,才能继续执行获取链接操做。this

测试

以后执行如下测试代码spa

public static void main(String[] args) {
    int allNum = 100;
    AtomicInteger successNum = new AtomicInteger(0);
    ConnectionPoolOfWaitNotify connectionPoolOfWaitNotify = new ConnectionPoolOfWaitNotify();
    IntStream.rangeClosed(1, allNum).parallel().forEach(i -> {
        Connection connection = null;
        try {
            connection = connectionPoolOfWaitNotify.getConnectioin(100);
            if (null != connection) {
                successNum.addAndGet(1);
                System.out.println("线程" + i + "拿到链接" + connection.getConnectionName());
                Thread.sleep(200);
            } else {
                System.out.println("线程" + i + "没有拿到链接");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != connection) {
                connectionPoolOfWaitNotify.revertConnection(connection);
            }
        }
    });

    System.out.println("总共拿链接次数:" + allNum + ",拿到链接次数:" + successNum.get());
}

allNum为总共获取链接次数,successNum当中记录获取成功的次数。每次拿链接的超时时间为100毫秒,拿到链接后200的休眠(模拟业务处理)以后归还链接。并且在构造函数中直接初始化了全部的链接(读者能够考虑下如何作到按需来初始化的话)。运行后结果以下线程

线程23拿到链接connection-1
线程69没有拿到链接
线程10拿到链接connection-2
线程74拿到链接connection-4
线程25拿到链接connection-3
线程6没有拿到链接
线程70没有拿到链接
线程72没有拿到链接
线程71没有拿到链接
线程73没有拿到链接
线程75拿到链接connection-1
总共拿链接次数:100,拿到链接次数:25

100次只能拿到25次,那咱们若是设置永不超时呢?调用方式以下,修改超时时间为0便可code

connection = connectionPoolOfWaitNotify.getConnectioin(0);

以后再次运行结果以下图片

线程70拿到链接connection-1
线程58拿到链接connection-2
线程16拿到链接connection-3
线程60拿到链接connection-4
线程71拿到链接connection-1
线程59拿到链接connection-4
线程67拿到链接connection-3
线程68拿到链接connection-2
总共拿链接次数:100,拿到链接次数:100

OK,所有拿到,没毛病ci

相关文章
相关标签/搜索