ReentrantLock实现PV操作-模拟多线程竞争数据库连接池资源场景

2023-11-14

使用ReentrantLock+Condition模拟PV操作,实现多线程竞争数据库连接池资源、资源耗尽后阻塞等待、归还资源后唤醒阻塞线程的场景(代码中为10个线程竞争5个数据库连接资源)

ConnectionPool.class(连接池)

package demo.lock.db;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;

/**
 * 模拟数据库连接池
 */
public class ConnectionPool {
    /** 连接池最大连接数量 */
    private static final int POOL_SIZE = 5;
    public static List<Connection> connections;
    /** 信号量 */
    public static volatile int signal = POOL_SIZE;
    /** 锁 */
    public static ReentrantLock lock = new ReentrantLock();
    /** 等待条件 */
    public static Condition condition = lock.newCondition();

    /**
     * <p>
     * 初始化数据库连接池
     * </p>
     */
    public static void init() {
        connections = new ArrayList<>(POOL_SIZE);
        for (int i = 0; i < POOL_SIZE; i++) {
            connections.add(new Connection("connection-" + i));
        }
    }

    /**
     * <p>
     * 使用ReentrantLock将获取数据连接逻辑包装为一个P操作原语
     * <p>
     * 抢占到锁后,如果检测到信号量不大于0,将自身加入Condition对象的阻塞队列中,并释放锁
     * </p>
     * <p>
     * 继续持有锁或被唤醒后重新持有锁时,尝试自旋来获取数据库连接
     * </p>
     * 
     * @return 获取到的数据库连接对象
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        // 进行P操作,首先获取锁对象
        lock.lock();
        try {
            // 此时抢占到锁对象,但由于信号量小于等于0,也就是没有资源可用,所以阻塞自身,同时释放锁
            if (signal <= 0) {
                condition.await();
            }
            // 持有或重新持有锁对象时,进行自旋操作尝试获取数据库连接
            while (true) {
                if (signal > 0) {
                    // 从连接池中获取一个空闲连接
                    List<Connection> freeConnections = connections.stream()
                        .filter(x -> x.state.equals(ConnectionState.FREE)).collect(Collectors.toList());
                    Connection currConnection =
                        CollectionUtils.isEmpty(freeConnections) ? null : freeConnections.get(0);
                    if (null == currConnection) {
                        return null;
                    }
                    currConnection.state = ConnectionState.BUSY;
                    // 获取连接成功,信号量自减1
                    signal--;
                    System.out.println("当前线程:" + Thread.currentThread().getName() + " 抢到数据库连接:"
                        + currConnection.getName() + " 当前连接池中空闲连接数:" + signal);
                    return currConnection;
                }
            }
        } finally {
            // 释放锁对象
            lock.unlock();
        }

    }

    /**
     * <p>
     * 使用ReentrantLock将归还数据连接逻辑包装为一个V原语
     * </p>
     * <p>
     * 在归还连接成功,并将信号量自增之后,唤醒Condition的等待队列中的一个线程
     * </p>
     * 
     * @param connection 数据库连接对象
     */
    public static void repayConnection(Connection connection) {
        if (null == connection) {
            return;
        }
        // 进行V操作,首先获取锁对象
        lock.lock();
        try {
            connections.forEach(x -> x.state = connection.equals(x) ? ConnectionState.FREE : x.state);
            // 信号量自增1,表示归还1个资源
            signal++;
            System.out.println("当前线程:" + Thread.currentThread().getName() + " 归还数据库连接:" + connection.getName()
                + " 当前连接池中空闲连接数:" + signal);
            // 唤醒该condition的等待队列中的一个线程
            condition.signal();
        } finally {
            // 释放锁资源
            lock.unlock();
        }
    }
}

Connection.class(连接对象)

package demo.lock.db;

/**
 * 数据库连接对象
 */
public class Connection {
    /**
     * 连接名
     */
    String name;
    /**
     * 当前状态
     */
    ConnectionState state = ConnectionState.FREE;

    public Connection(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "当前连接:" + ",状态:" + state.state;
    }
}

ConnectionState.class(连接状态枚举)

package demo.lock.db;

/**
 * 数据库连接当前状态枚举
 */
public enum ConnectionState {

    FREE(0, "空闲"), BUSY(1, "忙碌");

    /**
     * 状态码 0-空闲 1-忙碌
     */
    int code;
    /**
     * 状态名
     */
    String state;

    ConnectionState(int code, String state) {
        this.code = code;
        this.state = state;
    }
}

Application.class(程序入口)

package demo.lock;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import demo.lock.db.Connection;
import demo.lock.db.ConnectionPool;

public class Application {

    ThreadPoolExecutor executor =
        new ThreadPoolExecutor(10, 10, 30, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());

    public void execute() {
        for (int i = 0; i < 10; i++) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        Connection connection = ConnectionPool.getConnection();
                        // 随机占用0-1秒时间后归还连接
                        Thread.sleep((long)(Math.random() * 1000));
                        ConnectionPool.repayConnection(connection);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }

    }

    public void shutdown() {
        if (null == executor) {
            return;
        }
        executor.shutdown();
    }

    public static void main(String[] args) throws InterruptedException {
        ConnectionPool.init();
        Application application = new Application();
        application.execute();
        Thread.sleep(10000L);
        application.shutdown();
    }
}

运行结果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ReentrantLock实现PV操作-模拟多线程竞争数据库连接池资源场景 的相关文章

随机推荐

  • SLIP、PPP、PPPoE、L2TP以及PPTP协议

    SLIP协议 全称Serial Line Internet Protocol 串行线路网际协议 该协议是Windows远程访问的一种旧工业标准 主要在Unix远程访问服务器中使用 因为SLIP协议是面向低速串行线路的 可以用于专用线路 也可
  • threejs点击模型实现模型边缘高亮的选中效果--更改后提高帧率

    先来个效果图 之前写的那个稍微有点问题 帧率只有30 参照官方代码修改后 帧率可以达到50了 在不全屏的状态下 帧率60 1 首先需要导入库 用于模型边缘高亮 import EffectComposer from three example
  • 2023第十四届蓝桥杯国赛 C/C++ 大学 B 组

    省赛还水了个省一 国赛原型毕露了 参考文献 13条消息 2023第十四届蓝桥杯国赛 C C 大学 B 组 旧林墨烟的博客 CSDN博客 13条消息 2023第十四届蓝桥杯国赛 C C 大学 B 组 赛后记录 Zero的博客 CSDN博客 A
  • Python3:我低调的只用一行代码,就导入Python所有库!

    一行代码导入python所有库 1 引言 2 Pyforest 2 1 Pyforest 介绍 2 2 Pyforest 安装与使用 2 2 1 安装 2 2 2 使用 3 总结 1 引言 今天我们来分享一个懒人库 Pyforest 小屌丝
  • 数据分析:pandas

    pandas 常用数据类型 Series创建 Series切片和索引 DataFrame 读取外部数据 dataframe创建 dataframe基本属性查询 排序 取行列 布尔索引 字符串方法 缺失数据处理 数据合并 join merge
  • Week2 Git 入门1: Advanced git interaction

    通过cd 命令 进入一个git repository的目录 执行 atom psript py 用atom 打开当前repo里的psript py文件 改写sript py文件 可以直接使用git commit a m 命令提交并保存本次修
  • 谷粒商城项目总结--Elasticsearch

    Elasticsearch 一 基本概念 二 安装 三 初步检索 1 cat 2 索引一个文档 保存 3 查询文档 4 更新文档 5 删除文档 索引 6 bulk 批量 API 7 样本测试数据 四 进阶检索 1 检索信息 2 Query
  • rsync+nfs构建高可用文件系统详细步骤

    当系统要求高可靠 高性能时 一般采用分布式部署方案 应用服务器分布式部署比较成熟 应用中用到的文件 如文件 图片等上传下载 系统有如下几种方案 1 存储在文件目录 传统处理方式 2 存储到存储云上 按相关存储云的api开发即可 不涉及物理部
  • Github学生包申请(学校邮箱版,秒通过)

    Github学生包申请 学校邮箱版 一 注册邮箱 二 申请github学生包 一 注册邮箱 1 若已拥有学校邮箱可直接看下一 2 没有学校邮箱参考步骤去注册 1 浏览器搜索 自己学校名字 邮件系统 2 然后根据步骤自己注册 二 申请gith
  • Linux运维比较实用的工具

    1查看进程占用带宽情况 Nethogs Nethogs 是一个终端下的网络流量监控工具可以直观的显示每个进程占用的带宽 下载 http sourceforge net projects nethogs files nethogs 0 8 n
  • ajax2客户端需要那些jar包,最全最详尽的Ajax2

    一 数据类型 返回数据的处理 xhr responseText的返回是都是字符串 但有时候我们却需要数组类型 或者着json类型 实际后台可以传递一种伪数组 伪json的形式 但还是字符串如下例 1 leo Bob Gati 2 name
  • matlab作图两侧留白太多的问题

    用matlab作图时 经常发生图片两侧留白太多的情况 用LaTeX写论文时 插入图片如果两侧留白太多 会使图片整体变小 格式排版以及图片清晰度都大打折扣 下面总结两种解决方式 1 当图片为单张图片时 直接选择文件 gt 导出设置 在导出设置
  • 用空闲时间做了一个小程序

    一直在摸鱼中赚钱的大家好呀 自从接触了小程序开发之后 就想做一个自己的小程序项目 这不 从摸鱼时间中挤出了部分空闲时间不断完善和踩坑 一点点的墨迹出来了 由于我自己做出来的界面不能说富丽堂皇 但是确实也上不了台面 所以高价聘请知名设计师设计
  • zookeeper基础环境搭建及启动脚本

    zookeeper功能 1 可以为客户端管理少量数据 数据库 2 可以为客户端监听节点的状态 并在数据节点发生变化时通知客户端 3 场景 动态增加服务器 1 上传安装包 解压 tar zxvf name C apps 2 改名字 cp zo
  • 金山云AI新突破:集智高清让带宽降下去让画质升上来

    眼观六路 耳听八方 大脑 情绪和注意力均处在亢奋状态 运用各种招术攻击对手的同时也能巧妙防御 游戏直播为万千玩家提供了观摩高手过招的绝好机会 很多职业选手也由此拥有了大批粉丝 进一步增添了游戏的魅力 游戏如今已从亚文化向主流文化蔓延 而对于
  • 【构建ML驱动的应用程序】第 2 章 :制定计划

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 25 道常见的 TypeScript 面试题及答案

    前端Q 我是winty 专注分享前端知识和各类前端资源 乐于分享各种有趣的事 关注我 一起做个有趣的人 公众号 点击上方 前端Q 关注公众号 回复加群 加入前端Q技术交流群 编辑整理 杨小爱 TypeScript 是一种静态类型的 面向对象
  • Python爬虫获取Csdn文章

    request获取html 安装 pip install requests 使用 import requests HTTP请求 GET POST PUT DELETE HEAD OPTIONS get res requests get ht
  • YOLOv5(PyTorch)目标检测:原理与源码解析

    PyTorch版YOLOv5目标检测 原理与源码解析 课程链接 https edu csdn net course detail 31428 Linux创始人Linus Torvalds有一句名言 Talk is cheap Show me
  • ReentrantLock实现PV操作-模拟多线程竞争数据库连接池资源场景

    使用ReentrantLock Condition模拟PV操作 实现多线程竞争数据库连接池资源 资源耗尽后阻塞等待 归还资源后唤醒阻塞线程的场景 代码中为10个线程竞争5个数据库连接资源 ConnectionPool class 连接池 C