Apache-Common-Pool2中对象池的使用方式

最近在工作中,对几个产品的技术落地进行梳理。这个过程中发现一些朋友对如何使用Apache的对象池存在一些误解。所以在写作“业务抽象”专题的空闲时间里,本人觉得有必要做一个关于对象池的知识点和坑点讲解。Apache Common-Pool2 组件最重要的功能,就是向开发人员提供一个稳定的、高性能的对象池。我们在实际开发过程中最常用、最典型的对象池就是数据库连接池。对象池可以在数据库连接对象被调用前,预先生成和管理一批可以使用的对象,用空间换时间的方式提升应用程序对数据库的操作性能。

1、对象池的使用场景

1.1、因为创建/验证对象造成较明显时间/性能消耗的场景

我们在应用程序开发过程中,经常使用基于TCP协议的数据库连接进行数据库操作。建立数据库连接实际上是一个比较复杂的网络通讯过程,而我们通过创建好的连接进行数据库操作(无论是读操作还是写操作)都只是整个TCP通讯过程中比较简短的一个过程,示例过程如下:
在这里插入图片描述
【此图来源于网络】

如上图所示,在正式执行数据库操作时,作为开发人员来说实际上只关心黄色部分“sql执行”的实际操作过程,而不太会承认其余部分所消耗的时间和自己书写的代码性能有关。由此可知,如果我们在需要进行数据库操作时,才进行数据库连接的创建,然后再进行正式的数据库操作,除上图中黄色区域所代表的的实际操作外其余部分消耗的时间都是一种浪费。

为了改进这个过程,我们可以先创建一批可以正常使用的数据库连接对象放在一旁,等我们需要进行数据库操作时,再从之前已经创建好的数据库连接对象中取得一个进行使用。当我们完成了数据库操作后,如果确定使用的数据库连接仍然能正常工作,就没有必要销毁这个数据库连接,而可以重新放回到一旁待下一次需要时再重复使用。这样的做法是典型的用空间换时间的方式提升运行性能:预先生成的一批对象占用了应用系统的内存,但是节约了对象创建、验证、销毁所消耗的时间。

1.2、对象所代表的资源在系统中是某种紧俏的存在

在进行图像特效处理工作中,例如图像马赛克、图像去模糊、图像羽化渲染等这样的处理工作。我们会采用多张图像捕捉处理卡在硬件级别进行处理,例如一台主机的一个操作系统同时管理5张图像处理卡。每一张图像处理卡在高级编程语言中,都被映射成一个独立的对象,且这样的对象一旦进行初始化就会消耗大量的时间。类似这样的对象,我们就要使用对象池进行管理。

在这里插入图片描述
这种场景下,通常的例子还有数据库连接、资源桶对象、租户网关等。

1.3、对象可以被重复使用,且具有幂等性控制的场景

能够被对象池所管理的实际工作对象,自身一定是可以被重复使用的。这个前提读者应该是很好理解的,因为如果不能被重复使用,那么对象从对象池重复取出使用时一定会报错。好消息是,在我们实际工作过程中遇到的大部分场景,其主要对象都是可以重复使用的,例如:图形图像处理对象、线程对象、加密处理对象、物资仓库对象、货运车辆对象等等。

但并不是对象能够被重复使用,开发人员就可以毫无顾忌的将它放入对象池进行重复使用了。开发人员还需要注意保持这些被重复使用对象的幂等性,例如在重复使用socket套接字时,为了保证幂等性每次使用完成后都需要保证套接字的缓存区不存在还没有发送的或者接受的信息,并且保证被归还的socket套接字没有被关闭;在重复使用货运车辆对象时,必须保证被归还的货运车辆上没有剩余的货物,且货运车辆还有油量。

2、关于对象池的常见误解

  • 误解:对象池和线程池是一个概念

高级语言一般会给开发人员提供线程池这样的功能进行使用(例如Java、C#、C++),虽然表现形式有所区别,但原理上都是将有限的线程资源映射成一个对象,并放入一个池集中进行管理;还将需要执行的工作任务也映射成对象,再根据一定逻辑使用管理的线程进行工作任务运行。这样来看线程池和对象池是否都是一个概念呢?

线程池是一种特殊的对象池,是对操作系统的线程资源进行管理,并能够驱动CPU线程进行工作的一种对象池。

我们一般意义上使用的对象池,是将有限的对象放置于一个共享的池中,然后在多线程场景下供多个线程分享取用,如下图所示:
在这里插入图片描述

但线程池是将待运行的任务放置到一个池中,并对有限数量的线程状态进行控制,最后根据应用程序的运行需要控制这些任务在受控线程上的运行。其核心要点在于可以控制线程的运行和线程上具体执行的工作任务。如下图所示:
在这里插入图片描述

当然我们可以将线程池和普通的线程池进行集成工作——这也是在实际工作中我们经常采用的一种保持系统性能平衡的方式,如下图所示:
在这里插入图片描述

  • 误解:对象池中的对象永远不需要进行销毁

对象池的主要功能是为了重用对象提高调用性能。这些对象所代表的资源本身可能是远程的、异步的、不稳定的。也就是说这些在应用程序中被映射成对象的资源本身是容易失效的,如果资源本身已经失效,代表这个资源存在于应用系统中的对象也应该失效。所以,对象池中的对象需要经常进行有效性验证,一旦验证失败就需要进行销毁——毕竟保证系统的稳定性要比保证系统的性能重要很多。大多数对象池也提供了不同场景下的验证、销毁策略,在应用程序运行时进行工作——比如后文中将进行使用讲解的Apache-Common Pool2 线程池。

  • 误解:只要使用了对象池,应用程序性能就会得到提升

实际上使用了对象池并保证对象池中对象的稳定,只是提高应用程序性能的一个前提。只有对象池和配合其使用的线程池达到一个稳定的状态,才能保证应用程序性能的提升。注意:这里使用的是线程池,而不是线程。也就是说站在本文的视角来看,在应用程序中只要使用了对象池,就应该有和其配合使用的线程池

对于对象池能够提升性能的另一个误解是,对象池中的对象可以在功能调用时提高工作性能。这种理解和运用对象池的初衷也有冲突:对象池的设计目标是用空间换时间的方式提高性能。哪一部分时间呢?就是对象初始化、对象销毁这部分操作所耗费的时间。哪一部分空间呢?就是这些被预先创建好的,可以正常工作的对象,将会存放在应用程序的内存区域(可能是本地的也可能是远程的)。也就是说对象所代表的资源被调用时,该消耗多少时间还是会消耗多少时间。

3、对象池使用实例讲解

这里本文对Apache-Common-Pool2中提供的对象池进行使用讲解。

3.1、定义一个功能对象(必须)

由于Apche的对象池本身基于AQS进行实现,具体来说是基于Apache自身撰写的一个线程安全的LinkedBlockingDeque(org.apache.commons.pool2.impl.LinkedBlockingDeque)队列集合进行的功能实现。所以开发人员定义的将要在对象池中使用的对象,不必自己再处理线程安全性(除非开发人员自身制造了一个危险的运行环境),按照一般的对象进行定义即可:

// 这是一个将要被放入对象池中的真实对象
public class MyObject implements Closeable {
  // 最大操作次数
  private static final Integer MAX_OP = 10;
  // 对象在系统中唯一的一个id信息
  private String uuid;
  // 该池化对象被借用的次数
  // 当借用次数达到一定数量后,该对象必须被回收,一遍保证缓存区不会溢出
  private AtomicInteger count = new AtomicInteger(0);
  // 表示是否已经关闭
  private AtomicBoolean closed = new AtomicBoolean(false);
  
  public MyObject() {
    // 为这个对象生成一个唯一的id
    this.uuid = UUID.randomUUID().toString();
  }

  public String getUuid() {
    return uuid;
  }
  
  // 这是该对象正式完成工作的调用方法
  public void doSomething() {
    try {
      // 测试代码,使用System.out即可
      System.out.println(String.format("...... 模拟工作过程 ...... 对象:%s,进行了工作 ......" , this.uuid));
      // 停止随机的毫秒数,模拟业务处理过程
      Random random = new Random();
      synchronized (this) {
        this.wait(random.nextInt(100));
      }
    } catch(InterruptedException e) {
      e.printStackTrace(System.out);
    } finally {
      this.count.incrementAndGet();
    }
  }
  
  /**
   * 对象能够被多次操作10次,10次后这个对象就必须关闭</br>
   * 这主要是为了模拟一些带有本身带有缓存、操作实现的操作对象
   */
  public boolean validate() {
    return this.count.get() < MAX_OP && !this.closed.get();
  }

  // 获取这个对象当前的使用次数
  public Integer count() {
    return this.count.intValue();
  }
  
  public void close() throws IOException {
    // 表示该对象不再有效
    this.closed.set(true);
  }
}

3.2、定义这个对象的池化对象(非必须)

池化对象是对真实对象的包装,其目的在于对真实对象的状态、借出时间、创建时间等状态信息进行记录和监控。如果在进行对象池化动作时,需要加入一些特定操作,则开发人员可以继承DefaultPooledObject类(推荐)或者重新实现PooledObject接口(不推荐)。但整个过程并不是必须的,事实上池化对象的记录和监控工作最好不要进行干扰。

以下是一种池化对象的扩展方式(这里只是做一个扩展方式的讲解,后续的代码中不会使用这个自定义的MyPooledObject类):

// 可以自定义的池化对象
public class MyPooledObject extends DefaultPooledObject<MyObject> {

  public MyPooledObject(MyObject object) {
    super(object);
  }
}

3.3、定义池化对象工厂(必须)

池化对象工厂定义了这个对象在对象池中被要求进行创建、借出、回收、检查操作时,该对象该如何进行进行工作。

// 池化对象工厂
public class MyObjectFactory extends BasePooledObjectFactory<MyObject> {
  /**
   * 对象池要求生成对象时,该方法被触发
   */
  @Override
  public MyObject create() throws Exception {
    MyObject myObject = new MyObject();
    String uuid = myObject.getUuid();
    System.out.println(String.format("生成一个新的对象 : %s " , uuid));
    return myObject;
  }
  /**
   * 用于为真实对象生成池化对象
   */
  @Override
  public PooledObject<MyObject> wrap(MyObject obj) {
    DefaultPooledObject<MyObject> myObject = new DefaultPooledObject<MyObject>(obj);
    return myObject;
  }
  /**
   * 当对象池在创建新的池化对象时、借出池化对象前、归还池化对象前,需要对被借出的池化对象进行验证,验证这个池化对象是否还可用(状态正常,可以被使用),
   * 如果验证失败,则对象销毁方法(destroyObject)会被触发。
   * 特别注意:
   * 1、在创建新的池化对象时,且设置了TestOnCreate为true时,该方法才会被触发
   * 2、在借出池化对象时,且设置了TestOnBorrow为true时,该方法才会被触发
   * 3、在归还池化对象时,且设置了TestOnReturn为true时,该方法才会被触发
   */
  @Override
  public boolean validateObject(PooledObject<MyObject> p) {
    MyObject myObject = p.getObject();
    boolean validate = myObject.validate();
    System.out.println(String.format("**** 验证一个正在使用的对象 %s , 验证结果为 %b" , myObject.getUuid() , validate));
    return validate;
  }
  /**
   * 当对象池被要求借出池化对象时,该方法一定会被触发。在正式借出前,可以修改池化对象的一些业务状态,做一些准备工作。
   * 注意:</br>
   * 1、该方法如果抛出异常,则这个将要被借出的对象将被销毁。</br>
   * 2、该方法如果抛出异常,且这个将要被借出的对象是一个刚被创建的对象,则这个对象将被销毁且异常会被抛出</br>
   */
  @Override
  public void activateObject(PooledObject<MyObject> p) throws Exception {
    
  }
  /**
   * 当池化对象被归还时,该方法一定会被触发。在完成归还动作前,操作者可以在这个方法中完成一些业务状态操作,做一些“擦屁股”性质的工作
   * 注意:如果该方法抛出异常,则这个被刚归还还没有正式回归对象池的对象,将被销毁
   */
  @Override
  public void passivateObject(PooledObject<MyObject> p) throws Exception {
    
  }
  /**
   * 当对象池需要销毁当前池化对象时,该方法被触发。
   * 触发原因可能是多种的,可能是已经到了设置的最大借出数量,也可能是validateObject方法返回为false,
   * 也可能是activateObject方法或者passivateObject方法抛出了异常
   */
  @Override
  public void destroyObject(PooledObject<MyObject> p) throws Exception {
    MyObject myObject = p.getObject();
    try {
      String uuid = myObject.getUuid();
      Integer count =  myObject.count();
      System.out.println(String.format("XXXXXXX 销毁一个不能使用的对象 %s , 销毁时对象被使用了%s 次" ,uuid , count.toString()));
    } catch(Exception e) {
      e.printStackTrace(System.out);
    }
    // 进行对象内部的关闭操作
    myObject.close();
    super.destroyObject(p);
  }
}

3.4、进行对象池的使用

在这个示例中,我们创建一个线程池并在线程池的工作中,只用这个对象池。创建线程池是为了保证应用程序在多线程并行处理过程上和线程数量上能有一个性能平衡点。

public class App {
  
  public static void main(String[] args) {
    MyObjectFactory factory = new MyObjectFactory();
    GenericObjectPoolConfig<MyObject> config = new GenericObjectPoolConfig<MyObject>();
    config.setMaxTotal(2000);
    config.setMaxIdle(10);
    config.setMinIdle(5);
    // ...... 补充其它的
    // 当对象池中的池化对象耗尽,调用者是否阻塞等待执行,直到能过去到对象
    config.setBlockWhenExhausted(true);
    // 设置池化对象在创建、借出、归还时,是否验证池化对象的可用性
    config.setTestOnBorrow(false);
    config.setTestOnCreate(false);
    config.setTestOnReturn(true);
    // 池中空闲的对象,最早等待多久被回收(1分钟)
    config.setMinEvictableIdleTimeMillis(60000l);
    // 该池是否可以进行JMX监控
    config.setJmxEnabled(true);
    
    // 创建对象池
    final GenericObjectPool<MyObject> pool = new GenericObjectPool<MyObject>(factory, config);
    // 使用一个线程池,不停的调用
    LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
    ThreadPoolExecutor execute = new ThreadPoolExecutor(15, 30, 10l, TimeUnit.SECONDS, workQueue);
    for(int index = 0 ; index < 1000 ; index++) {
      execute.submit(new ExecuteTask(pool , index));
    }
  }
  
  // 执行任务
  private static class ExecuteTask implements Runnable {
    private GenericObjectPool<MyObject> pool;
    // 记录这是第多少个任务
    private int index;
    
    public ExecuteTask(GenericObjectPool<MyObject> pool , int index) {
      this.pool = pool;
      this.index = index;
    }
    
    @Override
    public void run() {
      // 取出对象,进行使用,并一定要还回
      MyObject myObject = null;
      try {
        myObject = this.pool.borrowObject();
        myObject.doSomething();
      } catch(Exception e) {
        e.printStackTrace(System.out);
      } finally {
        if(myObject != null) {
          // 这个20%的随机情况,模拟一部分使用者在使用过程中,主动关闭了对象
          Random random = new Random();
          if(random.nextFloat() <= 0.2f) {
            this.doClose(myObject);
          }
          this.pool.returnObject(myObject);
        }
        System.out.println(String.format("  ------- 第%d个任务完成工作  ------- " , this.index));
      }
    }
    
    private void doClose(MyObject myObject) {
      try {
        myObject.close();
      } catch (IOException e) {
        e.printStackTrace(System.out);
      }
    }
  }
}

注意:实际工作中,一定使用和对象池配套的线程池,才能达到提高性能的目的。 另外,经常看笔者文章的读者都知道,在正式工作中不能使用System.out进行输出。而这里之所以这样用,完全是为了简化依赖包的引入。

  • 这里对GenericObjectPoolConfig所提供的的配置信息含义进行一个简要介绍:
配置信息配置信息含义
maxTotal对象池中可容纳的最大对象数。
maxIdle对象池中允许存在的最大闲置对象数。
minIdle对象池中允许存在的最小闲置对象数。注意:maxTotal、maxIdle、minIdle三者的关系,还需要读者知晓对象池运行的过程。
blockWhenExhausted当对象池中的池化对象耗尽,调用者是否阻塞等待执行,直到能获取到对象。
testOnCreate在建立对象时检测对象是否有效(默认为true) , 一些资料提到配置true会导致性能下降,纯粹乱说,这只是检测时机的问题。
testOnBorrow在从对象池获取对象时是否检测对象有效(默认为true) ,一些资料提到配置true会导致性能下降,纯粹乱说,这只是检测时机的问题。
testOnReturn在向对象池中归还对象时是否检测对象有效(默认为true) ,一些资料提到配置true会导致性能下降,纯粹乱说,这只是检测时机的问题。根据实际情况笔者个人偏向在onReturn的事件发生时,进行对象有效性校验。
testWhileIdle在对象池中的对象空闲下来后,是否检测对象的有效性(默认为false)。如果开发人员使用对象池所管理的对象本身存在一个比较复杂的检测逻辑,来判定对象是否失效,那么可以将该属性设置为true。这样对象池会专门有一个“闲置检测线程”来独立进行对象是否有效的检测。
timeBetweenEvictionRunsMillis如果设置testWhileIdle为true,该属性则设定“闲置检测线程”对对象池中的闲置对象的检测周期。
minEvictableIdleTimeMillis对象池中的闲置对象最小空闲时间,达到此值后该空闲对象可能会被移除。不过是否要移除,还需看是否已达设定的最大空闲数(示例中设置的是1分钟)
jmxEnabled是否打开对象池的JMX监控,相关的属性还有:jmxNamePrefix、jmxNameBase
  • 下面对使用过程中的常用调用方法进行介绍:在对象池被创建后,GenericObjectPool对象就是开发人员直接调用的对象了,其中主要调用的方法包括:
方法名方法介绍
addObject调用该方法一般用于初始化对象池,给对象池添加一个对象。如果对象池已满,则什么操作都不做。在多数场景下,该方法实际上不用被显示调用,除非使用者对于该对象池的使用有很强的性能方面的考虑。
borrowObject从对象池取得一个对象,该方法用注意两个细节——a、blockWhenExhausted=false,如果对象池中的对象数量已经达到maxTotal设定的数量,立即抛出NoSuchElementException异常——b、blockWhenExhausted=true,如果对象池中的对象数量已经达到maxTotal设定的数量,会等待maxWaitMillis毫秒,之后还是没有获取到对象则抛出NoSuchElementException异常。
returnObject归还一个对象到对象池。
invalidateObject从对象池删除一个对象,如果有线程在等待对象,则会再创建一个对象。
getNumIdle返回空闲对象个数。
getNumActive返回被使用的对象个数。
clear清除所有的空闲对象,释放闲置资源。
close关闭对象池,释放对象池所有资源,close之后borrowObject会产生异常,returnObject不会产生异常。

以下是示例程序可能的运行结果:

生成一个新的对象 : d50850bb-491b-4558-92dc-2e49c8cecefd 
生成一个新的对象 : 77bb46f7-3254-4310-9298-afb025edecab 
生成一个新的对象 : 08bf9e4d-c0c1-4d78-a64c-5eb8b1c79dc7 
生成一个新的对象 : dce1d2ea-0adb-42db-8ad0-96356799554d 
生成一个新的对象 : d6d1e75f-0d63-45e8-b3a5-3396f980a8c3 
生成一个新的对象 : b8e46e6f-58fb-4006-970e-c15fe10d1e36 
生成一个新的对象 : 2c9608ff-d1fb-4b13-a967-c3cad67a957f 
生成一个新的对象 : 2fdc5b30-8376-4d02-9b9c-63da0e2e6f35 
生成一个新的对象 : 3340d781-3c75-42c9-add2-725b09814870 
生成一个新的对象 : ff4a8f65-7d4f-4ada-bbad-20c6dff08784 
生成一个新的对象 : b08d2a59-a8e2-4bdd-bf28-2fc9bf239333 
生成一个新的对象 : 1d94099b-ae7a-4beb-b291-3eeb74576454 
生成一个新的对象 : 7c04de5f-e82e-4b29-a4c7-41eb2b273294 
生成一个新的对象 : 2f5fab6e-287c-4416-938b-6132c030e371 
生成一个新的对象 : 21122161-f8f9-421b-ad3b-6eabc5aadff3 
...... 模拟工作过程 ...... 对象:2f5fab6e-287c-4416-938b-6132c030e371,进行了工作 ......
...... 模拟工作过程 ...... 对象:d6d1e75f-0d63-45e8-b3a5-3396f980a8c3,进行了工作 ......
...... 模拟工作过程 ...... 对象:3340d781-3c75-42c9-add2-725b09814870,进行了工作 ......
.......... 省略日志内容 ..........
...... 模拟工作过程 ...... 对象:d50850bb-491b-4558-92dc-2e49c8cecefd,进行了工作 ......
...... 模拟工作过程 ...... 对象:21122161-f8f9-421b-ad3b-6eabc5aadff3,进行了工作 ......
**** 验证一个正在使用的对象 7c04de5f-e82e-4b29-a4c7-41eb2b273294 , 验证结果为 true
**** 验证一个正在使用的对象 b08d2a59-a8e2-4bdd-bf28-2fc9bf239333 , 验证结果为 false
**** 验证一个正在使用的对象 2fdc5b30-8376-4d02-9b9c-63da0e2e6f35 , 验证结果为 true
**** 验证一个正在使用的对象 21122161-f8f9-421b-ad3b-6eabc5aadff3 , 验证结果为 true
XXXXXXX 销毁一个不能使用的对象 b08d2a59-a8e2-4bdd-bf28-2fc9bf239333 , 销毁时对象被使用了1 次
  ------- 第10个任务完成工作  ------- 
  ------- 第7个任务完成工作  ------- 
  ------- 第12个任务完成工作  ------- 
  ------- 第13个任务完成工作  ------- 
...... 模拟工作过程 ...... 对象:2fdc5b30-8376-4d02-9b9c-63da0e2e6f35,进行了工作 ......
...... 模拟工作过程 ...... 对象:7c04de5f-e82e-4b29-a4c7-41eb2b273294,进行了工作 ......
...... 模拟工作过程 ...... 对象:21122161-f8f9-421b-ad3b-6eabc5aadff3,进行了工作 ......
生成一个新的对象 : 03195a9b-c651-47a6-b905-4b87b8af915a 
...... 模拟工作过程 ...... 对象:03195a9b-c651-47a6-b905-4b87b8af915a,进行了工作 ......
**** 验证一个正在使用的对象 2c9608ff-d1fb-4b13-a967-c3cad67a957f , 验证结果为 true
**** 验证一个正在使用的对象 b8e46e6f-58fb-4006-970e-c15fe10d1e36 , 验证结果为 false
  ------- 第1个任务完成工作  ------- 
**** 验证一个正在使用的对象 08bf9e4d-c0c1-4d78-a64c-5eb8b1c79dc7 , 验证结果为 true
XXXXXXX 销毁一个不能使用的对象 b8e46e6f-58fb-4006-970e-c15fe10d1e36 , 销毁时对象被使用了1 次
**** 验证一个正在使用的对象 ff4a8f65-7d4f-4ada-bbad-20c6dff08784 , 验证结果为 true
  ------- 第3个任务完成工作  ------- 
  ------- 第9个任务完成工作  ------- 
...... 模拟工作过程 ...... 对象:2c9608ff-d1fb-4b13-a967-c3cad67a957f,进行了工作 ......
  ------- 第2个任务完成工作  ------- 
...... 模拟工作过程 ...... 对象:ff4a8f65-7d4f-4ada-bbad-20c6dff08784,进行了工作 ......
...... 模拟工作过程 ...... 对象:08bf9e4d-c0c1-4d78-a64c-5eb8b1c79dc7,进行了工作 ......
生成一个新的对象 : 6b798bf7-1a91-40c8-b035-90756f086795 
...... 模拟工作过程 ...... 对象:6b798bf7-1a91-40c8-b035-90756f086795,进行了工作 ......

.......... 省略日志内容 ..........

后续有时间我们再来详细说明对象池的内部运行过程。

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

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

相关文章

【快速解决】保姆级Anaconda安装教程

目录 第一步 ​编辑第二步 ​编辑第三步 第四步 第五步 第六步 ​编辑 第七步 第八步 第九步 第一步 在anaconda清华大学开源软件镜像站下载anaconda。点击这里进入 我这里选的是windows-x86_64。 第二步 下载好以后进行安装 第三步 第四步 第五步 选择…

【操作系统】在阅读论文:OrcFS: Orchestrated file system for flash storage时需要补充的基础知

在阅读论文&#xff1a;OrcFS: Orchestrated file system for flash storage是需要补充的基础知识 这篇论文是为了解决软件层次之间的信息冗余问题 To minimize the disk traffic, the file system buffers the updates and then flushes them to the disk as a single unit, …

通达信波动指数指标公式,识别盘整还是趋势

波动指数(Choppiness Index)是由澳大利亚商品交易员E.W. Dreiss开发的技术指标&#xff0c;用来判断市场是盘整还是趋势。该指标属于非方向性指标&#xff0c;不用于判断市场方向&#xff0c;而仅用于识别市场趋势。 指标的取值范围为0到100&#xff0c;数值越高&#xff0c;表…

[足式机器人]Part2 Dr. CAN学习笔记-Advanced控制理论 Ch04-12+13 不变性原理+非线性系统稳定设计

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Advanced控制理论 Ch04-1213 不变性原理非线性系统稳定设计 1. Invariance Princilpe-LaSalle;s Theorem不变性原理2. Nonlinear Basic Feedback Stabilization 非线性系统稳定设计 1. Invarianc…

Visual Studio Code1.67版本已正式发布,新增Rust指南

Visual Studio Code1.67版本已正式发布&#xff0c;该版本包含大量增强生产力的更新项&#xff1a; 资源管理器文件嵌套 通过这次更新&#xff0c;用于浏览和管理文件和文件夹的Visual Studio Code的资源管理器工具现在支持基于名称嵌套相关文件。 资源管理器现在支持根据文…

python学习笔记10(选择结构2、循环结构1)

&#xff08;一&#xff09;选择结构2 1、if……else……语句 #&#xff08;1&#xff09;基本格式 numbereval(input("请输入您的6位中奖号码&#xff1a;")) if number123456:print("恭喜您&#xff0c;中奖了") else:print("未中奖")#&…

JVM内存结构 vs. Java对象模型 vs. Java内存模型

文章目录 0.三者的区别1.JVM内存结构2.Java对象模型3.Java内存模型&#xff08;JMM&#xff09;3.1 为什么需要JMM3.2 JMM是规范3.3 JMM是工具类和关键字的原理3.4 最重要的三点内容 0.三者的区别 JVM内存结构&#xff1a;和Java虚拟机的运行时区域有关。 Java对象模型&#…

负荷预测 | Python基于CEEMDAN-VMD-BiGRU的短期电力负荷时间序列预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 提出一种分解去噪、重构分解的 CEEMDAN-VMD-BiGRU组合预测方法&#xff1a; 1 采用CEEMDAN将原始电力负荷数据分解成一组比较稳定的子序列&#xff0c;联合 小波阈值法将含有噪声的高频分量去噪&#xff0c;保留含有信…

竞赛练一练 第29期:GESP和电子学会相关题目练习

CIE一级2021.09_无奈的Jaime 小宝在房间吵闹&#xff0c;妈妈让Jaime想办法安抚小宝。Jaime在房间来回走动思考方法。最后也没想出方法只能摊开双手说 “sorry&#xff01;”。 1. 准备工作 &#xff08;1&#xff09;添加背景&#xff1a;Bedroom 3&#xff1b; &#xff0…

[解决方案]运行时错误‘53’,文件未找到:MathPage.WLL

问题描述 mathtype使用报错,运行时错误‘53’,文件未找到:MathPage.WLL 解决方案 一、首先确定自己电脑的位数(这里默认大家的电脑都是64位) 二、右击MathType桌面图标,点击“打开文件所在位置”,然后找到MathPage.WLL文件所在位置 MathPage.WLL位置:D:\mathtype\…

Video接口介绍

屏库 https://m.panelook.cn/index_cn.php Open LDI, open lvds display interface OpenLDI and LVDS是兼容的&#xff0c; 是一种电平 https://www.ti2k.com/178597.html MIPI DSI/Camera crosLink FPD-LINK(Flat panel display link)是National(TI) LVDS技术&#xff0c; …

EI论文复现:考虑多能互补的综合能源系统/虚拟电厂/微电网优化运行程序代码!

本程序参考EI论文《基于多能互补的热电联供型微网优化运行》&#xff0c;文章通过储能设备解耦热电联系&#xff0c;建立基于多能互补的综合能源系统/虚拟电厂/微电网优化运行模型。模型包含系统供给侧的多能互补协调与需求侧的综合能源响应两个方面&#xff0c;使供给侧通过能…

Java内存模型之重排序

文章目录 1.什么是重排序2.重排序的好处3.重排序的三种情况4.用volatile修正重排序问题 1.什么是重排序 首先来看一个代码案例&#xff0c;尝试分析一下 x 和 y 的运行结果。 import java.util.concurrent.CountDownLatch;/*** 演示重排序的现象&#xff0c;直到达到某个条件…

解决MPICH的GPU初始化失败:一次深入探索

今天来分享“MPICH&#xff1a;MPII_Init_thread(222): gpu_init failed”这个问题的解决方式 文章目录 前言问题原因解决方案 前言 如果在安装MPICH的时候没有注意要一些选项&#xff0c;那么当使用mpicxx mpi_send.cpp -o send && mpirun -n 2 ./send进行编译输出的…

Linux之静态库和动态库

目录 一、前言 二、对于库的理解 三、静态库 四、动态库 五、动静态库的加载 一、前言 在之前&#xff0c;我们讲了静态库和动态库&#xff0c;详情请跳转&#xff1a;静态库和动态库 下面我们将从工程师的角度&#xff0c;去了解静态库和动态库的形成过程&#xff0c;以…

Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?

JPG还是PNG&#xff1f; JPG和PNG是两种常见的图片文件格式&#xff0c;在压缩方式、图像质量、透明效果和可编辑性等方面存在显著差异。 压缩方式&#xff1a;JPG是一种有损压缩格式&#xff0c;通过丢弃图像数据来减小文件大小&#xff0c;因此可能会损失一些图像细节和质量…

华为设备登录安全配置案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle O…

【现代密码学】笔记9-10.3-- 公钥(非对称加密)、混合加密理论《introduction to modern cryphtography》

【现代密码学】笔记9-10.3-- 公钥&#xff08;非对称加密&#xff09;、混合加密理论《introduction to modern cryphtography》 写在最前面8.1 公钥加密理论随机预言机模型&#xff08;Random Oracle Model&#xff0c;ROM&#xff09; 写在最前面 主要在 哈工大密码学课程 张…

Java Http各个请求类型详细介绍

1. 前言 在Spring Boot框架中&#xff0c;HTTP请求类型是构建Web应用程序的重要组成部分。常见的请求类型包括GET、POST、PUT和DELETE&#xff0c;每种类型都有其特定的用途和特点。本文将详细比较这四种请求类型&#xff0c;帮助您在开发过程中做出明智的选择。 2. GET请求…

Java SPI在数据库驱动、SpringBoot自动装配中的应用

文章目录 1. 初识SPI1.1 SPI的作用1.2 SPI的工作原理1.3 SPI的三个组件&#xff1a;Service、Service Provider、ServiceLoader1.4 SPI使用场景1.5 具体的SPI 源码分析&#xff08;SPI的核心就是ServiceLoader.load()方法&#xff09;1.6 SPI 的优缺点 2. API、SPI、JNDI释义3.…
最新文章