等待超时最佳实例

一般对于昂贵的资源,我们都会选择使用等待超时模式

为什么要使用等待超时?

假设说我们现在使用tomcat,配置了固定的100个线程去处理客户端请求,然后又一个数据库连接池,连接池中维护了10个连接。现在有10个线程获取到了连接,他们都要占用这个连接10秒,那么其他线程如果要一直等待下去的话,这个代价非常的大。客户端不可能等待你一个请求那么就,且你一个请求一直占用住一个线程,会导致新的请求进不来,没办法给客户端进行响应。所以说我们一般要使用等待超时模式,如果超过某一时间获取不到连接词,通知客户端繁忙,也能将该线程让出来去处理新的请求,这样对于用户体验来说有相比之前有巨大的提升。

 

数据库连接池等待超时实现

import java.sql.Connection;
import java.util.LinkedList;
import java.util.Queue;


public class ConnectionPool {
    private final int size;
    private final Queue<Connection> list = new LinkedList<>();

    public ConnectionPool(int size) {
        this.size = size;
        init();
    }

    private void init() {
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                list.add(DriverManager.createManager());
            }
        }
    }

    public Connection getConnection(long mills) throws InterruptedException {
        synchronized (list) {
            if (mills <= 0) {
                while (list.isEmpty()) {
                    list.wait();
                }
                return list.poll();
            } else {
                long future = System.currentTimeMillis() + mills;
                long remaining = mills;
                while (list.isEmpty() && remaining > 0) {
                    list.wait(mills);
                    remaining = future - System.currentTimeMillis();
                }
                if (list.isEmpty()) {
                    return null;
                }
                return list.poll();
            }
        }
    }

    public int getConnectionCount() {
        return list.size();
    }

    public synchronized void releaseConnection(Connection connection) {
        if (connection != null) {
            synchronized (list) {
                list.add(connection);
                // 释放一个连接就唤醒一个线程来获取连接
                list.notify();
            }
        }
    }
}

在并发编程的艺术书中,其调用了notifyAll()方法,其实我不是很明白,为什么要调用notifyAll()。

我觉得就这个场景来说,notify()方法完全足购,因为一次只能释放一个资源,所以最多唤醒一个waitSet中的线程来获取连接,所以我这里使用了notify()方法,可能是某些东西没有想到。

还有上面的getConnection()方法,之前想过这样的实现

public Connection getConnection(long mills) throws InterruptedException {
    synchronized (list) {
        if (mills <= 0) {
            while (list.isEmpty()) {
                list.wait();
            }
            return list.poll();
        } else {
            if(list.isEmpty()){
                list.wait(mills);
            }
            if(list.isEmpty()){
                return null;
            }
            return list.poll();
        }
    }
}

这个逻辑其实是错的,为什么?

按照之前的想法来说,如果当前线程被notify唤醒,从waitSet中加入到了entrySet中,那么他就能获取到一个Connection
如果当前线程因为超时唤醒,list就是空,直接返回null。感觉上没什么问题。
我们忽略了一个非常重要的细节,当其他线程notify一个线程之后,说明增加了一个资源
notify到一个线程(也就是说该线程在超时之前被notify唤醒),但是并不代表当前线程马上就能获取到锁,如果有EntrySet中的其他线程先获取到锁,那么其就会先将List中的连接池使用了,那么这个被notify唤醒的线程就不能拿到了,他就返回了null
但是他可能并没有等到milli那么久(他还可以继续等待,但是因为被notify唤醒了导致等待结束),导致了失败率变高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值