Apache Commons Pool的对象池技术

第1章:引言

咱们今天来聊聊一个在Java开发中超级实用,但又经常被忽视的技术——对象池技术。可能你们已经听说过“对象池”这个名词,但对它的具体作用和重要性还有些模糊。别急,小黑带你们一步步深入了解。

想象一下,咱们的程序就像一个忙碌的餐厅,每次客人点餐都得现做一套餐具,吃完后就扔掉,是不是很浪费?如果有了对象池,就好比餐厅里有一堆干净的餐具备着,客人来了直接用,用完洗干净再放回去,等待下一个客人使用。这样既节约了制造餐具的时间,又减少了浪费。

在Java世界里,创建对象(比如餐具)是件耗费资源的事。如果频繁地创建和销毁对象,不仅浪费时间,还会给垃圾回收器带来压力。这时候,对象池技术就闪亮登场了。它允许咱们重复使用一组已经创建好的对象,大大提升了性能和资源利用效率。

第2章:对象池技术基础

对象池就像一个仓库,里面存放着一堆预先创建好的对象。当程序需要对象时,就从池子里“借”一个,用完后再“归还”回去,而不是直接扔掉。这样一来,对象就可以被多次重用,减少了创建和销毁对象的开销。

那对象池的优势到底在哪里呢?首要的当然是性能提升。想象一下,如果每次处理请求都要新建一个数据库连接,那系统的响应时间肯定会变长,但如果连接已经在池子里准备好了,直接拿来用,就快多了。其次是资源利用的优化。对象一旦创建,就占用了内存资源。频繁地创建和销毁对象,不仅增加了垃圾回收的压力,还可能导致内存碎片。使用对象池,可以更好地管理和复用这些资源。

对象池在Java中的应用非常广泛,比如数据库连接池、线程池等。这些都是为了提高资源利用效率和程序性能。咱们举个例子来看看对象池在实际中是怎么运作的。想象一下,你要做一道菜,需要一些食材。你可以每次都去超市买(每次都创建对象),也可以在家里备好一些常用的(对象池)。显然,后者更高效。

讲到这里,咱们可能会好奇,对象池是怎么管理这些对象的呢?通常情况下,对象池会有几个关键的操作:创建对象、借用对象、归还对象和销毁对象。对象池会维护一个池子,里面放着一些已经创建好的对象。当程序请求对象时,池子会提供一个空闲的对象;用完后,对象不是被销毁,而是被归还到池子中,等待下一次使用。

接下来,小黑给大家演示一个简单的对象池示例。在这个例子里,我们会创建一个小型的对象池,用来管理字符串对象。代码如下(注释会用中文写,方便大家理解):

import java.util.concurrent.ConcurrentLinkedQueue;

public class SimpleStringPool {
    // 创建一个线程安全的队列,用于存储字符串对象
    private ConcurrentLinkedQueue<String> pool = new ConcurrentLinkedQueue<>();

    // 初始化对象池,预先填充一些字符串对象
    public SimpleStringPool(int size) {
        for (int i = 0; i < size; i++) {
            pool.add("对象" + i);  // 这里就是创建对象的过程
        }
    }

    // 从池子中借用一个对象
    public String borrowObject() {
        String object = pool.poll();  // 从队列中取出一个对象
        if (object == null) {
            // 如果池子空了,就新建一个对象(在实际应用中应避免)
            object = "新对象";
        }
        return object;
    }

    // 归还对象到池子中
    public void returnObject(String object) {
        pool.offer(object);  // 把对象放回队列
    }
}

这个简单的对象池示例展示了对象池的基本原理:预先创建对象、从池中借用、使用后归还。虽然这个例子很简单,但它却揭示了对象池技术的核心思想:重用和资源管理。

第3章:Apache Commons Pool概览

Commons Pool的核心组件

讲到核心组件,Commons Pool主要由几个关键接口和类组成。最核心的是ObjectPool接口,它定义了对象池的基本操作,比如获取对象(borrowObject)、返回对象(returnObject)等。实际上,咱们在使用Commons Pool时,大部分时间都是在和这个接口打交道。

然后是PooledObjectFactory接口,这是一个用于创建和管理池对象生命周期的工厂接口。通常,当咱们需要将自定义的对象放入对象池时,就需要实现这个接口。

一个简单的Apache Commons Pool示例

让咱们通过一个实际的例子来看看Apache Commons Pool是怎么工作的。假设小黑想创建一个字符串对象的池。下面是基本的代码示例:

import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.PooledObject;

public class StringPoolFactory implements PooledObjectFactory<String> {

    @Override
    public PooledObject<String> makeObject() throws Exception {
        // 创建对象
        return new DefaultPooledObject<>(new String("Hello, World"));
    }

    @Override
    public void destroyObject(PooledObject<String> p) throws Exception {
        // 销毁对象时的操作
    }

    @Override
    public boolean validateObject(PooledObject<String> p) {
        // 验证对象是否可用
        return true;
    }

    @Override
    public void activateObject(PooledObject<String> p) throws Exception {
        // 激活对象时的操作
    }

    @Override
    public void passivateObject(PooledObject<String> p) throws Exception {
        // 钝化对象时的操作
    }
}

public class SimpleStringPoolDemo {
    public static void main(String[] args) throws Exception {
        // 创建一个对象池
        ObjectPool<String> pool = new GenericObjectPool<>(new StringPoolFactory());

        // 从池中借用对象
        String str = pool.borrowObject();
        System.out.println(str);  // 输出:"Hello, World"

        // 使用完毕,归还对象
        pool.returnObject(str);
    }
}

在这个例子中,StringPoolFactory是一个自定义的工厂,用于创建和管理字符串对象。然后咱们使用GenericObjectPool这个类来创建一个对象池,并将自定义的工厂传给它。这样,咱们就可以从池中借用字符串对象,并在使用完毕后归还。

第4章:核心组件和架构

核心组件解析

Apache Commons Pool的核心组件主要包括两大部分:对象工厂对象池

  1. 对象工厂(PooledObjectFactory)

    • 这个接口负责生产和管理池中的对象。
    • 它定义了几个关键的方法,比如makeObject()用于创建新对象,destroyObject()用于销毁对象,还有validateObject()activateObject()passivateObject()等,分别用于在对象借出和归还时对其进行验证和状态管理。
  2. 对象池(ObjectPool)

    • 对象池接口定义了操作池对象的基本方法,例如borrowObject()用于从池中借用对象,returnObject()用于将对象归还到池中。
    • Apache Commons Pool提供了几种对象池的实现,如GenericObjectPool是最常用的一种,它提供了丰富的配置选项,使得池的行为可以高度定制。
对象池的工作流程

对象池的工作流程相对简单直观。当程序请求一个对象时,池会先检查是否有可用的对象。如果有,就直接将其提供给程序;如果没有,池就会请求对象工厂创建一个新的对象。一旦对象使用完毕,它不会被销毁,而是被归还到池中,等待下一次使用。

代码示例:深入对象池的工作原理

为了更好地理解这一过程,小黑准备了一个更详细的示例。这次,我们将创建一个稍微复杂一点的对象池,用于管理数据库连接(假设的简化版):

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;

// 模拟的数据库连接
class FakeConnection {
    private String id;

    public FakeConnection(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    // 模拟执行数据库操作
    public void execute() {
        System.out.println("执行数据库操作, 使用连接: " + id);
    }
}

// 数据库连接的工厂
class FakeConnectionFactory implements PooledObjectFactory<FakeConnection> {
    private int counter = 0;

    @Override
    public PooledObject<FakeConnection> makeObject() throws Exception {
        // 创建新的连接
        return new DefaultPooledObject<>(new FakeConnection("连接" + (++counter)));
    }

    @Override
    public void destroyObject(PooledObject<FakeConnection> p) throws Exception {
        // 销毁连接的操作(在这里是空的,实际应用中可能需要关闭连接)
    }

    @Override
    public boolean validateObject(PooledObject<FakeConnection> p) {
        // 验证连接是否有效
        return true;
    }

    @Override
    public void activateObject(PooledObject<FakeConnection> p) throws Exception {
        // 激活连接(在这里不做任何操作)
    }

    @Override
    public void passivateObject(PooledObject<FakeConnection> p) throws Exception {
        // 钝化连接(在这里不做任何操作)
    }
}

public class DatabaseConnectionPoolDemo {
    public static void main(String[] args) throws Exception {
        // 创建一个连接池
        GenericObjectPool<FakeConnection> pool = new GenericObjectPool<>(new FakeConnectionFactory());

        // 从池中借用连接
        FakeConnection conn = pool.borrowObject();
        conn.execute();  // 使用连接执行操作

        // 使用完毕,归还连接
        pool.returnObject(conn);
    }
}

在这个例子中,FakeConnection代表一个模拟的数据库连接,FakeConnectionFactory是它的工厂。小黑使用GenericObjectPool来创建一个连接池,并通过工厂来管理这些连接。

通过这个例子,咱们可以看到,Apache Commons Pool不仅仅是管理简单对象那么简单。它能够有效地管理复杂资源,比如数据库连接,并且提供了高度的可配置性,以适应各种不同的需求。

第5章:实际应用与代码示例

实际应用场景

在实际开发中,对象池技术经常被用于管理那些创建成本高昂、需要频繁使用的资源。例如,数据库连接池是一个典型的应用场景。数据库连接的建立通常需要消耗较多的时间和资源,如果每次数据操作都重新建立连接,不仅效率低下,还会给数据库服务器带来极大压力。因此,使用连接池来复用这些连接就显得非常必要。

示例:数据库连接池

接下来的例子中,小黑将展示如何使用Apache Commons Pool来实现一个简单的数据库连接池。虽然这里用的是模拟的数据库连接,但它的原理和实际应用是相同的。

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

// 模拟数据库连接
class MyDatabaseConnection {
    private String connectionString;

    public MyDatabaseConnection(String connectionString) {
        this.connectionString = connectionString;
    }

    // 模拟数据库操作
    public void executeQuery(String query) {
        System.out.println("执行查询: " + query + " 在连接: " + connectionString);
    }
}

// 数据库连接工厂
class MyDatabaseConnectionFactory implements PooledObjectFactory<MyDatabaseConnection> {
    private String connectionString;

    public MyDatabaseConnectionFactory(String connectionString) {
        this.connectionString = connectionString;
    }

    @Override
    public PooledObject<MyDatabaseConnection> makeObject() throws Exception {
        return new DefaultPooledObject<>(new MyDatabaseConnection(connectionString));
    }

    // 其他必要的方法实现略...
}

public class DatabaseConnectionPoolDemo {
    public static void main(String[] args) throws Exception {
        // 配置池的参数
        GenericObjectPoolConfig<MyDatabaseConnection> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(10); // 最大连接数
        config.setMaxIdle(5);   // 最大空闲连接数
        config.setMinIdle(2);   // 最小空闲连接数

        // 创建连接池
        GenericObjectPool<MyDatabaseConnection> pool = new GenericObjectPool<>(
                new MyDatabaseConnectionFactory("jdbc:fake:mydatabase"), config);

        // 从池中获取连接
        MyDatabaseConnection conn = pool.borrowObject();
        conn.executeQuery("SELECT * FROM my_table");

        // 使用完毕,归还连接
        pool.returnObject(conn);
    }
}

在这个例子中,MyDatabaseConnection是模拟的数据库连接类,MyDatabaseConnectionFactory用于创建这些连接。通过配置GenericObjectPoolConfig,咱们可以控制池的大小、空闲连接数等参数,从而优化资源利用和性能。

第6章:高级特性与最佳实践

高级特性探索
  1. 配置选项

    • Apache Commons Pool提供了大量的配置选项,让你能够根据应用的需求调整对象池的行为。比如,你可以设置最大池大小(setMaxTotal)、最大空闲对象数(setMaxIdle)、最小空闲对象数(setMinIdle)等等。这些配置项帮助你平衡性能和资源消耗,达到最佳效果。
  2. 对象验证

    • 对象验证是一个重要的特性,它确保了从池中借出的对象是有效的。通过实现PooledObjectFactoryvalidateObject方法,你可以定义自己的验证逻辑,比如检查一个数据库连接是否仍然开放。
  3. 激活和钝化

    • activateObjectpassivateObject方法允许你在对象被借出时执行特定操作(比如重置对象状态),以及在对象归还时执行清理操作。
  4. 逐出策略

    • 逐出策略允许你定义如何从池中移除不再需要的对象。例如,你可以设置一个定时任务,定期检查并逐出长时间空闲的对象。
代码示例:高级配置

让我们通过代码看看这些高级特性是如何运用的。假设小黑正在实现一个数据库连接池,我们需要配置一些高级选项:

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

// ... [MyDatabaseConnection 和 MyDatabaseConnectionFactory 的定义,见前面的章节]

public class AdvancedConnectionPoolDemo {
    public static void main(String[] args) throws Exception {
        GenericObjectPoolConfig<MyDatabaseConnection> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(20); // 设置最大连接数
        config.setMaxIdle(10);  // 设置最大空闲连接数
        config.setMinIdle(5);   // 设置最小空闲连接数
        config.setTestOnBorrow(true); // 借出对象时进行验证

        // 设置逐出策略参数
        config.setTimeBetweenEvictionRunsMillis(60000); // 逐出任务执行间隔时间
        config.setMinEvictableIdleTimeMillis(300000);  // 最小逐出空闲时间

        GenericObjectPool<MyDatabaseConnection> pool = new GenericObjectPool<>(
                new MyDatabaseConnectionFactory("jdbc:fake:mydatabase"), config);

        // 使用连接池...
    }
}

最佳实践
  1. 合理配置对象池

    • 根据应用的实际负载调整池的大小和其他参数。过大的池会浪费资源,而过小的池则可能导致性能问题。
  2. 对象的生命周期管理

    • 确保对象在被借出、归还和逐出时的状态正确管理,避免资源泄露或无效对象的使用。
  3. 监控和调试

    • 对象池的监控也很重要,它可以帮助你理解池的状态和性能。Apache Commons Pool提供了一些工具来监控池的状态,比如活跃对象数、空闲对象数等。

第7章:性能考量和调优

性能考量因素
  1. 池大小

    • 池的大小直接影响资源的使用和性能。如果池太大,会浪费资源;如果太小,则可能因为频繁地创建和销毁对象而影响性能。合理的池大小取决于应用的负载和具体需求。
  2. 对象创建和销毁的成本

    • 如果对象的创建和销毁成本很高,使用对象池可以带来显著的性能提升。但如果这些操作的成本很低,对象池的好处可能就不那么明显了。
  3. 并发级别

    • 对象池在高并发场景下的表现是一个重要的考量点。在高并发下,需要确保池能够快速、有效地分配和回收对象。
调优策略
  1. 合理配置池参数

    • 根据应用的实际需求调整池的大小、最大空闲对象数、最小空闲对象数等参数。
  2. 监控池的性能

    • 使用JMX或其他工具来监控对象池的状态,如活跃对象数、空闲对象数、等待借用对象的线程数等。
  3. 调整对象的生命周期管理

    • 通过优化PooledObjectFactory中的方法来减少对象状态管理的开销。
  4. 使用合适的逐出策略

    • 配置合理的逐出策略,定期移除不活跃的对象,释放资源。
代码示例:性能监控

让我们通过一个简单的例子来看看如何对Apache Commons Pool进行性能监控:

import org.apache.commons.pool2.impl.GenericObjectPool;

// ... [MyDatabaseConnection 和 MyDatabaseConnectionFactory 的定义]

public class PerformanceMonitoringDemo {
    public static void main(String[] args) throws Exception {
        GenericObjectPool<MyDatabaseConnection> pool = new GenericObjectPool<>(new MyDatabaseConnectionFactory("jdbc:fake:mydatabase"));

        // 配置和使用连接池...

        // 执行性能监控
        System.out.println("活跃连接数: " + pool.getNumActive());
        System.out.println("空闲连接数: " + pool.getNumIdle());
        System.out.println("等待借用连接的线程数: " + pool.getNumWaiters());

        // 在实际应用中,可能需要更复杂的监控逻辑
    }
}

在这个例子中,我们通过GenericObjectPoolgetNumActivegetNumIdlegetNumWaiters方法来获取当前池的状态。这些信息对于理解池的性能和调整配置是非常有用的。

第8章:常见问题和解决方案

常见问题一览
  1. 资源耗尽

    • 问题:当所有的对象都在使用中,新的借用请求可能会导致等待,甚至超时。
    • 解决方案:调整池的大小和阻塞等待时间,或者为池配置更合适的对象创建策略。
  2. 对象泄露

    • 问题:如果借出的对象没有被正确归还,会导致对象泄露,长期下去可能耗尽池资源。
    • 解决方案:确保所有借出的对象都被归还。可以通过设置借用对象的最大使用时长来避免。
  3. 性能瓶颈

    • 问题:不合理的池配置可能导致性能瓶颈。
    • 解决方案:通过监控和分析池的状态来调整配置,如池大小、逐出策略等。
  4. 无效对象

    • 问题:池中的对象可能因为长时间未使用而变得无效。
    • 解决方案:实现有效的对象验证逻辑,以确保借出的对象是可用的。
代码示例:处理资源耗尽

来看一个具体的例子,如何处理资源耗尽的情况。假设咱们有一个数据库连接池,需要确保即使在高负载下也能平稳运行:

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

// ... [MyDatabaseConnection 和 MyDatabaseConnectionFactory 的定义]

public class ResourceExhaustionHandlingDemo {
    public static void main(String[] args) throws Exception {
        GenericObjectPoolConfig<MyDatabaseConnection> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(20);    // 设置最大连接数
        config.setBlockWhenExhausted(true);  // 当资源耗尽时,阻塞等待
        config.setMaxWaitMillis(5000);       // 设置最大等待时间为5000毫秒

        GenericObjectPool<MyDatabaseConnection> pool = new GenericObjectPool<>(new MyDatabaseConnectionFactory("jdbc:fake:mydatabase"), config);

        // 尝试从池中借用连接
        try {
            MyDatabaseConnection conn = pool.borrowObject();
            // 使用连接...

            pool.returnObject(conn);  // 归还连接
        } catch (Exception e) {
            // 处理可能的等待超时或其他异常
            System.out.println("处理异常:" + e.getMessage());
        }
    }
}

在这个例子中,咱们设置了池在资源耗尽时的行为:阻塞等待,最大等待时间为5000毫秒。如果在这段时间内仍然无法借用到对象,就会抛出异常。通过这种方式,咱们可以有效地管理资源的使用,避免因为无限等待而导致的系统崩溃。

第9章:总结

通过本文,咱们对Apache Commons Pool有了一个全面的了解。记住,对象池不是一个万能的解决方案,它需要根据具体场景和需求来合理使用和配置。希望这些知识能够帮助大家在日常开发中提高效率,解决实际问题。最后,小黑鼓励大家在工作中积极尝试和应用这些技术,不断提升自己的技能。祝大家编程愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/275508.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

k8s集群etcd备份与恢复

一、前言 k8s集群使用etcd集群存储数据&#xff0c;如果etcd集群崩溃了&#xff0c;k8s集群的数据就会全部丢失&#xff0c;所以需要日常进行etcd集群数据的备份&#xff0c;预防etcd集群崩溃后可以使用数据备份进行恢复&#xff0c;也可用于重建k8s集群进行数据恢复 二、备份…

0基础学习VR全景平台篇第132篇:曝光三要素—快门速度

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 经过前面两节课的学习我们认识了曝光三要素中的感光度和光圈&#xff0c;这节课我们将一同去了解影响曝光的最后一个要素——快门速度。 (曝光三要素&#xff1a;感光度、光圈、…

docker学习(二十、network使用示例host、none)

文章目录 一、host应用示例总结 二、none应用示例总结 network相关内容&#xff1a; docker学习&#xff08;十八、network介绍&#xff09; docker学习&#xff08;十九、network使用示例bridge&#xff09; docker学习&#xff08;二十、network使用示例host、none&#xff0…

【自然语言处理】第3部分:识别文本中的个人身份信息

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

在uniapp中使用背景渐变色与背景图不生效问题

list上有文字详情以及背景图&#xff0c;从背景可以看出是渐变色和 背景图片的结合。 因为使用到渐变色&#xff0c;所以要结合 background-blend-mode 属性来实现与背景图片叠加显示&#xff0c;否则只通过 background: linear-gradient(); background-image: url(); 设置不会…

关于Redis面试题

前言 之前为了准备面试&#xff0c;收集整理了一些面试题。 本篇文章更新时间2023年12月27日。 最新的内容可以看我的原文&#xff1a;https://www.yuque.com/wfzx/ninzck/cbf0cxkrr6s1kniv Redis 是什么 全名&#xff1a;远程字典服务。这是一个开源的在内存中的数据结构存…

算法测试流程脚本工具站

&#x1f349;一、录入sql和批量上传minIo图片&#xff0c; 录入sql&#xff08;掠过&#xff09;..., 一个上传&#xff0c;一个下载&#xff0c;只需上传&#xff0c;找到 def upload():# NOTE&#xff1a;Step:3 把拉下来的图片传上去给XXX服务器的minioup_data_minio(&q…

用CHAT写环保为主题的校园实践活动报告

问CHAT&#xff1a;写一篇以环保为主题的校园实践活动报告 CHAT回复&#xff1a;尊敬的老师&#xff0c;亲爱的同学们&#xff1a; 大家好&#xff0c;今天我很荣幸地向大家分享一下&#xff0c;最近我们团队在学校里所组织的环保主题实践活动。 环保是一件至关重要的事情&am…

web自动化(4)——POM设计重构

1. 什么是POM Page Object Model 是ui自动化测试中常见的封装方式。 原理&#xff1a;将页面封装为PO对象&#xff0c;然后通过面向对象的方式实现UI自动化 2. 封装原则 PO无需包含全部UI元素PO应当验证元素PO不应该包含断言PO不应该暴露元素 3. 怎么进行POM封装 面向对象…

Leetcode 763 划分字母区间

题意理解&#xff1a; 要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 输入&#xff…

大数据与人工智能|信息技术产业架构、行业发展与前沿技术(第2节)

内容链接&#xff1a;信息技术产业架构、行业发展与前沿技术&#xff08;大数据与人工智能系列课程 第2节&#xff09; 声明&#xff1a;学习使用&#xff0c;侵权必删&#xff01; 主要内容&#xff1a;1. 从算盘到量子计算机&#xff0c;介绍了半导体行业的发展历程和技术原…

二分查找——OJ题(一)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、二分查找1、题目讲解2、算法原理3、代码实现 二、在排序数组中查找元素的第一个和最后一个…

【本地缓存篇】如何实现本地缓存?

如何实现本地缓存? ✔️典型解析✔️数据结构✔️线程安全✔️对象上限✔️清除策略✔️过期时间 ✔️扩展知识仓基于Caffeine实现本地缓存 ✔️典型解析 所谓本地缓存&#xff0c;就是和应用服务器在一起的缓存工具&#xff0c;将需要缓存的数据放到本地缓存中&#xff0c;可…

【轻松入门】OpenCV4.8 + QT5.x开发环境搭建

引言 大家好&#xff0c;今天给大家分享一下最新版本OpenCV4.8 QT5 如何一起配置&#xff0c;完成环境搭建的。 下载OpenCV4.8并解压缩 软件版本支持 CMake3.13 或者以上版本 https://cmake.org/ VS2017专业版或者以上版本 QT5.15.2 OpenCV4.8源码包 https://github.com/op…

常用的 linux 命令

常用的 linux 命令 1.从其他机器拷贝文件夹2.查看哪个程序在用特定端口3.实时监控日志文件内容4.查看指定用户拥有的进程5.查看磁盘空间使用情况6.文件搜索which&#xff08;whereis&#xff09; 显示系统命令所在目录find 查找任何文件或目录1&#xff09; 根据文件名称查找2)…

【Linux驱动】Linux中断(一)—— 设备树中断节点

裸机使用中断需要通过寄存器手动配置&#xff0c;但有了 Linux 系统后&#xff0c;Linux内核提供了完善的中断框架&#xff0c;我们只需要申请中断&#xff0c;然后注册中断服务函数即可。 一、设备树中断属性 既然驱动中要注册中断服务函数&#xff0c;我们首先需要知道三个点…

实战 | 使用OpenCV快速去除文档中的表格线条(步骤 + 源码)

导 读 本文主要介绍如何使用OpenCV快速去除文档中的表格线条,并给详细步骤和代码。 背景介绍 测试图如下,目标是去除下面三张图中的表格线条,方便后续图像处理。 实现步骤 下面演示详细步骤,以图1为例: 【1】获取二值图像:加载图像、转为灰度图、OTSU二值化 i…

记录 | ubuntu源码编译python3.7.3(指定版本)

一、安装依赖包 sudo apt-get install -y make build-essential libssl-dev zlib1g-dev sudo apt-get install -y libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm sudo apt-get install -y libncurses5-dev libncursesw5-dev xz-utils tk-dev 二、从Python网…

mapboxgl 中给地图添加遮罩蒙版,并不遮罩其中一块区域

文章目录 概要效果预览技术思路技术细节小结 概要 本篇文章主要是给一整块地图添加遮罩层蒙版&#xff0c;但是不遮罩其中一个区域&#xff0c;以反向高亮地区内容。 效果预览 技术思路 这里要实现某个区域反显高亮&#xff0c;需要这个区域的边界json文件&#xff0c;与ech…

Flink1.17实战教程(第三篇:时间和窗口)

系列文章目录 Flink1.17实战教程&#xff08;第一篇&#xff1a;概念、部署、架构&#xff09; Flink1.17实战教程&#xff08;第二篇&#xff1a;DataStream API&#xff09; Flink1.17实战教程&#xff08;第三篇&#xff1a;时间和窗口&#xff09; Flink1.17实战教程&…