【二十六】springboot实现多线程事务处理

2023-11-01

 springboot篇章整体栏目: 


【一】springboot整合swagger(超详细

【二】springboot整合swagger(自定义)(超详细)

【三】springboot整合token(超详细)

【四】springboot整合mybatis-plus(超详细)(上)

【五】springboot整合mybatis-plus(超详细)(下)

【六】springboot整合自定义全局异常处理

【七】springboot整合redis(超详细)

【八】springboot整合AOP实现日志操作(超详细)

【九】springboot整合定时任务(超详细)

【十】springboot整合redis实现启动服务即将热点数据保存在全局以及redis(超详细)

【十一】springboot整合quartz实现定时任务优化(超详细)

【十二】springboot整合线程池解决高并发(超详细,保你理解)

【十三】springboot整合异步调用并获取返回值(超详细)

【十四】springboot整合WebService(超详细)

【十五】springboot整合WebService(关于传参数)(超详细)

【十六】springboot整合WebSocket(超详细)

【十七】springboot整合WebSocket实现聊天室(超详细)

【十八】springboot实现自定义全局异常处理

【十九】springboot整合ElasticSearch实战(万字篇)

【二十】springboot整合过滤器实战

【二十一】springboot整合拦截器实战并对比过滤器

【二十二】springboot整合activiti7(1) 实战演示篇

【二十三】springboot整合spring事务详解以及实战

【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据

【二十五】springboot整合jedis和redisson布隆过滤器处理缓存穿透

【二十六】springboot实现多线程事务处理_springboot多线程事务

【二十七】springboot之通过threadLocal+参数解析器实现同session一样保存当前登录信息的功能


        在前面二十四章做了一个springboot使用EasyExcel和线程池实现多线程导入Excel数据的demo,在写时忘了做事务处理,评论区有个大佬提出来了,这章就对二十四章的代码做一个改造,完善多线程的事务处理。

        对于springboot的事务处理,前面在二十三章也做过springboot整合spring事务详解以及实战的学习,但是在多线程时,这个东西并不适用,本章就通过手写事务处理(编程式事务处理)。

        由于本章是针对二十四章的批量导入功能的扩展,所有不会再写事务处理不相关的(二十四章的内容)介绍了。


qq交流群导航——>231378628

目录

一、阐述目的与实现方式

二、手动让子线程报错

三、改造主线程

四、改造子线程

五、测试


一、阐述目的与实现方式

        前面章节实现的多线程处理excel导入功能,如果一个子线程出现错误,结果会是那个子线程的数据处理不了,而其他子线程的数据仍然正常处理保存,并不会存在事务处理的情况,本章改造代码实现事务处理,所有线程正常执行才会保存数据,否则就回滚。大致如下:

二、手动让子线程报错

        为了后面测试事务回滚,手动让某个子线程报错,比如名为线程3的子线程,如下:


 三、改造主线程

        根据上面图的思路,首先改造主线程的代码,整体代码如下:

package com.swagger.demo.service;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.swagger.demo.config.SpringJobBeanFactory;
import com.swagger.demo.mapper.DeadManMapper;
import com.swagger.demo.model.entity.DeadManExcelData;
import com.swagger.demo.thread.DeadManThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author zrc
 * @version 1.0
 * @description: TODO 最新入狱名单导入监听器
 *
 * @date 2022/5/30 15:56
 */
@Service
@Slf4j
@Component
public class DeadManExcelListener extends AnalysisEventListener<DeadManExcelData> {

    /**
     * 多线程保存集合,使用线程安全集合
     */
    private List<DeadManExcelData> list = Collections.synchronizedList(new ArrayList<>());

    /**
     * 创建线程池必要参数
     */
    private static final int CORE_POOL_SIZE = 10; // 核心线程数
    private static final int MAX_POOL_SIZE = 100; // 最大线程数
    private static final int QUEUE_CAPACITY = 100; // 队列大小
    private static final Long KEEP_ALIVE_TIME = 1L; // 存活时间

    public List<DeadManExcelData> getData(){
        return list;
    }

    public DeadManExcelListener(){

    }

    public void setData(List<DeadManExcelData> deadManExcelDataList){
        this.list = deadManExcelDataList;
    }

    @Override
    public void invoke(DeadManExcelData deadManExcelData, AnalysisContext analysisContext) {
        if(deadManExcelData!=null){
            list.add(deadManExcelData);
        }
    }

    /**
     * 多线程方式保存
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("解析结束,开始插入数据");

        // 创建线程池
        ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 指定每个线程需要处理的导入数量,假设每个线程处理15000条,注意配合上面线程池的大小
        int singleThreadDealCount = 15000;

        // 根据假设每个线程需要处理的数量以及总数,计算需要提交到线程池的线程数量
        int threadSize=(list.size()/singleThreadDealCount)+1;

        // 计算需要导入的数据总数,用于拆分时线程需要处理数据时使用
        int rowSize = list.size() + 1;

        // 测试开始时间
        long startTime = System.currentTimeMillis();

        // 申明该线程需要处理数据的开始位置
        int startPosition = 0;

        // 申明该线程需要处理数据的结束位置
        int endPosition = 0;

        // 为了让每个线程执行完后回到当前线程,使用CountDownLatch,值为线程数,每次线程执行完就会执行countDown方法减1,为0后回到主线程,也就是当前线程执行后续的代码
        CountDownLatch count = new CountDownLatch(threadSize);

        // 用来控制主线程回到子线程
        CountDownLatch mainCount = new CountDownLatch(1);

        // 用来控制最终回到主线程
        CountDownLatch endCount = new CountDownLatch(threadSize);

        // 用来存放子线程的处理结果,若出错就保存一个false
        CopyOnWriteArrayList<Boolean> sonResult = new CopyOnWriteArrayList<Boolean>();

        // 使用线程安全的对象存储,保存主线程最后总的判断结果,是提交还是回滚
        AtomicBoolean ifSubmit = new AtomicBoolean(true);

        // 计算每个线程要处理的数据
        for(int i=0;i<threadSize;i++){
            // 如果是最后一个线程,为保证程序不发生空指针异常,特殊判断结束位置
            if((i+1)==threadSize){
                // 计算开始位置
                startPosition = (i * singleThreadDealCount);
                // 当前线程为划分的最后一个线程,则取总数据的最后为此线程的结束位置
                endPosition = rowSize-1;
            }else{
                // 计算开始位置
                startPosition = (i * singleThreadDealCount);
                // 计算结束位置
                endPosition = (i + 1) * singleThreadDealCount;
            }

            DeadManMapper deadManMapper = SpringJobBeanFactory.getBean(DeadManMapper.class);

            DeadManThread thread = new DeadManThread(count,deadManMapper,list,startPosition,endPosition
            ,sonResult,mainCount,ifSubmit,endCount);
            executor.execute(thread);
        }
        try {
            count.await();
            for (Boolean resp : sonResult) {
                if (!resp) {
                    // 只要有一个子线程出异常,就设置最终结果为回滚
                    log.info("主线程:有线程执行失败,所有线程需要回滚");
                    ifSubmit.set(false);
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 回到子线程处理回滚或者提交事务
            mainCount.countDown();
        }

        try {
            endCount.await();
            // 逻辑处理完,关闭线程池
            executor.shutdown();
            long endTime = System.currentTimeMillis();
            log.info("总耗时:"+(endTime-startTime));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

        新增如下4个参数(count是前面章节的已有的)。

PS:CountDownLatch类前面有讲过,通过await和countDown方法能够方便的实现多个线程之间的来回切换。

CopyOnWriteArrayList和AtomicBoolean是为了能够线程安全的保存多个线程共同使用的数据。 

        接着,重写DeadManThread线程类的构造方法,将上面新增的四个参数通过构造方法传给子线程。然后调用记录子线程第一次的cout的await方法,等待子线程第一次执行完毕,回到主线程继续执行。回到主线程后,主线程判断子线程第一次执行完后保存的返回集,判断是否存在false(子线程若报错,保存false,否则保存true)。若存在false就将idsubmit设置为false,意思是需要回滚数据,然后调用记录主线程执行的mainCount的countDown方法,让主线程执行完毕,回到子线程调用mainCount.countDown的位置继续子线程执行。

        当子线程根据ifSubmit判断完进行回滚还是提交事务操作后,回到主线程,主线程关闭线程池。

四、改造子线程

        接着改造子线程,整体代码如下:

package com.swagger.demo.thread;

import com.swagger.demo.config.SpringJobBeanFactory;
import com.swagger.demo.mapper.DeadManMapper;
import com.swagger.demo.model.entity.DeadMan;
import com.swagger.demo.model.entity.DeadManExcelData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author zrc
 * @version 1.0
 * @description TODO
 * @date 2022/7/22 15:40
 */
@Component
@Slf4j
public class DeadManThread implements Runnable{

    /**
     * 当前线程需要处理的总数据中的开始位置
     */
    private int startPosition;

    /**
     * 当前线程需要处理的总数据中的结束位置
     */
    private int endPosition;

    /**
     * 需要处理的未拆分之前的全部数据
     */
    private List<DeadManExcelData> list = Collections.synchronizedList(new ArrayList<>());

    /**
     * 记录子线程第一次执行是否完成
     */
    private CountDownLatch count;

    private DeadManMapper deadManMapper;

    /**
     * 保存每个线程的执行结果
     */
    private CopyOnWriteArrayList<Boolean> sonResult;

    /**
     * 记录主线程是否执行过判断每个线程的执行结果这个操作
     */
    private CountDownLatch mainCount;

    /**
     * 记录主线程对每个线程的执行结果的判断
     */
    private AtomicBoolean ifSubmit;

    /**
     * 声明该子线程的事务管理器
     */
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * 声明该线程事务的状态
     */
    private TransactionStatus status;

    /**
     * 记录子线程第二次执行是否完成
     */
    private CountDownLatch endCount;

    public DeadManThread() {

    }

    public DeadManThread(CountDownLatch count, DeadManMapper deadManMapper, List<DeadManExcelData> list
            , int startPosition, int endPosition, CopyOnWriteArrayList<Boolean> sonResult,CountDownLatch mainCount
    ,AtomicBoolean ifSubmit,CountDownLatch endCount) {
        this.startPosition = startPosition;
        this.endPosition = endPosition;
        this.deadManMapper = deadManMapper;
        this.list = list;
        this.count = count;
        this.sonResult = sonResult;
        this.mainCount = mainCount;
        this.ifSubmit = ifSubmit;
        this.endCount = endCount;
    }

    @Override
    public void run() {
        try{
            dataSourceTransactionManager = SpringJobBeanFactory.getBean(DataSourceTransactionManager.class);
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            status = dataSourceTransactionManager.getTransaction(def);
            if(Thread.currentThread().getName().contains("3")){
                throw new RuntimeException("线程3出问题了");
            }
            List<DeadMan> deadManList = new ArrayList<>();
            List<DeadManExcelData> newList = list.subList(startPosition, endPosition);
            // 将EasyExcel对象和实体类对象进行一个转换
            for (DeadManExcelData deadManExcelData : newList) {
                DeadMan deadMan = new DeadMan();
                BeanUtils.copyProperties(deadManExcelData, deadMan);
                deadManList.add(deadMan);
            }
            // 批量新增
            deadManMapper.insertBatchSomeColumn(deadManList);
            sonResult.add(true);
            } catch (Exception e) {
            e.printStackTrace();
            sonResult.add(false);
            } finally {
            // 当一个线程执行完了计数要减一不然这个线程会被一直挂起
            count.countDown();
            try {
                log.info(Thread.currentThread().getName() + ":准备就绪,等待其他线程结果,判断是否事务提交");
                mainCount.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ifSubmit.get()) {
                dataSourceTransactionManager.commit(status);
                log.info(Thread.currentThread().getName() + ":事务提交");
            } else {
                dataSourceTransactionManager.rollback(status);
                log.info(Thread.currentThread().getName() + ":事务回滚");
            }
            // 执行完所有逻辑,等待主线程执行
            endCount.countDown();
        }
    }
}

        先改造构造方法,用于接受主线程传过来的参数,并设置到自己的内部私有。

        然后改造run方法。

        先开启自己的事务,并保存事务状态,用于后面执行提交或者回滚操作。数据处理完后,若正常结束将线程安全的返回值集合变量保存一个true,否则保存false,并执行记录第一线程执行的count的countDown方法,等待所有子线程执行完后,返回主线程执行刚才上面讲的判断sonResult的代码,等主线程执行完判断并设置ifSubmit的值后,回到子线程执行main.await之后的代码。

        如果ifSubmit是false就回滚,否则就提交,执行完后执行记录子线程第二次执行的endCount的countDown方法,等待子线程全部执行完后,回到主线程,主线程执行关闭线程池的逻辑,结束。 

五、测试

        代码写完,测试一下。测试之前数据:

        现在是线程3会报错,调用接口测试。

        事务成功回滚,数据没有提交。若删除手动抛异常的代码,让程序正常执行,如下:

         数据提交成功,事务正常处理。

补充: 关于可能出现的死锁问题(子线程数大于10时发生),可能是springboot默认的datasource:
    hikari:
      maximum-pool-size

这个属性导致的,maximum-pool-size:最大连接数,小于等于0会被重置为默认值10。

hikari是springboot默认使用的数据源连接池。

        如果子线程是大于10个,但是最大连接数只有10,就会导致后面的子线程连不上数据库,10个连接上数据库的线程也无法释放,导致出现死锁。

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

【二十六】springboot实现多线程事务处理 的相关文章

  • 是否可以使 Spring Security 会话失效?

    我正在使用 Tomcat 6 0 32 Spring Security 3 0 5 在我的网络应用程序中 某些用户可以更改其他用户的权限 发生这种情况时 我想使权限已更改的用户的任何会话无效 这可能吗 如果可能的话怎么办 通常 您无法在更改
  • 如何在 Java 9 中使用新的 BeanInfo 注解

    JEP 256 BeanInfo 注释 http openjdk java net jeps 256为JavaBean http download java net java jdk9 docs api java beans JavaBea
  • Spring Security 上的组和 acl

    我想使用 Spring Security 来管理用户 组和权限 我想使用 ACL 来保护我的域对象 但我找不到将组分配给 acl 的方法 例如 我有用户和组 每个组可以拥有以下证券 管理论坛 可以是类似的角色ROLE FORUM MANAG
  • 如何将 Excel 中的图表导出为图形

    我有一系列 Excel 电子表格 每个电子表格至少包含一页数据和一页根据数据创建的图表 我需要捕获 不从数据中重新生成 将现有图表作为网络友好图像 这可以通过 Java 或 Net 实现吗 我知道 POI 的东西 Java 不会这样做 或者
  • JUnit 测试方法无法返回值

    为什么 JUnit 测试方法无法返回值 文档 https junit org junit5 docs current user guide writing tests classes and methods说 强调我的 测试方法和生命周期方
  • java.lang.IllegalArgumentException:比较方法违反了其一般契约[重复]

    这个问题在这里已经有答案了 您好 下面是我的比较器的比较方法 我不确定出了什么问题 我在堆栈溢出上查找了其他类似标题的问题和答案 但不确定我的方法出了什么问题 但我不断收到 java lang IllegalArgumentExceptio
  • 如何将 openapi-generator 中的客户端包含在 gradle java 应用程序中?

    我想创建一个 gradle java 应用程序 它从 openAPI 规范文件生成客户端并使用该客户端 所以我创建了一个java应用程序gradle init 类型 应用程序 语言 Java DSL groovy 测试框架 Junit Ju
  • 设置 MetaspaceSize 的指南 - java 8

    64 位服务器的 MetaspaceSize 默认值是多少 我在官方文档中没有找到它 我观察到 在服务器 JVM 进程中 GC 频率有时会变高并持续增长 如果我重新启动服务几次 它就会恢复稳定 我认为这是由于 JRE 升级造成的 JVM 堆
  • Spring Data (JPA) 多个存储库,没有很多类

    在我当前的项目中 我使用 Spring Data JPA 并且有 20 多个 Entity类 我想为它们创建存储库 但创建另一个类 每个类适用于任何模型 Repository注释似乎是某种矫枉过正和大量 重复 代码 所有存储库类将如下所示
  • 何时使用 clone() 以及 addAll() 和 add() 的实际工作原理

    我正在使用 Java 和 MySQL 我的项目中有大约 60 个交易屏幕 我曾经用过add and addAll 复制的功能ArrayList 例如 List
  • 改造添加带有令牌和 ID 的标头

    我在获取经过身份验证的用户时遇到问题 在此之前我得到了令牌和用户 ID 现在我需要使用访问令牌和 ID 从服务器获取用户 我有标题格式 https i stack imgur com OQ87Y png 现在我尝试使用拦截器添加带有用户令牌
  • 测试 Hessian remoting-servlet.xml

    我们使用 Hessian 来实现富客户端和服务器之间的通信 由于移动和重命名 remoting servlet xml 中的条目有时会与实际的类名不匹配 因此 我正在寻找一种简单的方法来测试远程处理 xml 有没有简单的方法可以做到这一点
  • Android 改造参数化@Headers

    我正在使用 OAuth 每次发出请求时都需要将 OAuth 令牌放入标头中 我看到 Header注释 但是有没有办法让它参数化 以便我可以在运行时传入 这是概念 Header Authorization OAuth var api vers
  • 使用 https 的 Java Jersey RESTful Web 服务

    我是 Java EE 的新手 正在开发一个 RESTful API 其中每个 API 调用用户都会发送编码的凭据 我的问题是如何通过默认的 http 实现 https 协议并确保我的连接安全 我正在使用 Jersey Restful Web
  • 如何在 Google 地图中创建自定义地图?

    我正在尝试创建一个包含我家地图的 Google 地图应用程序 卧室 浴室 厨房等 使用 GPS 我会找到我现在在家里的位置 并尝试获取到我卧室的方向 步行距离 您可以使用Google的API来获取方向 我需要知道的是 如何添加我家的自定义地
  • Spring Oauth2. DaoAuthenticationProvider 中未设置密码编码器

    我对 Spring Oauth 和 Spring Security 很陌生 我正在尝试在我的项目中使用 client credentials 流程 现在 我设法使用自己的 CustomDetailsS ervice 来从系统中已存在的数据库
  • 如何在非Spring的构造型类中使用@Autowired

    我想在此类中使用该存储库 但是当我放置像 Component 这样的构造型时 我从 IDE 收到错误 无法自动装配 未找到 身份验证 类型的 bean public class CustomMethodSecurityExpressionR
  • 使用基于Optional内容的流

    我从不受我控制的服务获取可能为空的地图 并且想要处理它 比方说 过滤 映射并减少到我需要的单个元素 问题 是否有从Optional到Stream的 链接 我尝试过 除其他外 return Optional ofNullable getMap
  • POJO 支持使用omnifaces 自动完成primefaces

    我正在尝试在我的项目中使用 primefaces 自动完成组件 以避免将特定转换器写入我尝试使用的每个列表对象全能面孔 http showcase omnifaces org converters ListConverter如建议的here
  • Java:将秒转换为分钟、小时和天[重复]

    这个问题在这里已经有答案了 任务是 输出应如下所示 最好回显输入 您输入了 500 000 秒 即 5 天 18 小时 53 分钟 20 秒 5天18 53 20小时 我该怎么做呢 最容易理解和做到的方法是什么 讲师还说 没有硬编码 我不太

随机推荐

  • 小程序内嵌webview实现支付

    点击上方 青年码农 关注 回复 特效源码 可获取各种资料 目前的一个小程序项目需要把客户之前的h5页面嵌入到现在的小程序中 并且之前的支付功能要正常 小程序提供了webview开放能力供我们使用 但是不允许在webview直接调起微信支付
  • 重载的分析

    前言 在c 中 出现重载的概念 其实就是一个高级语言的象征 他的出现就是机器语言更加的自然化 他其实可以理解位我们自然语言中的动词 它可以和不同的名词起到不同的功能 重载 定义 用一个相同的函数名来定义不同的函数 重载的条件 参数的个数不同
  • CMSIS-RTOS的信号量使用备忘

    说明 因为要使用CMSIS RTOS的信号量 所以需要了解以下几点功能 1 接收信号量时 返回值的意思 2 接收信号量时 如果信号量容器不只为一 那么是否可以再次接收到 3 发送信号量是否有限制 带着以上问题做了一个测试程序 例一 程序代码
  • strace ltrace记录

    strace 安装 常用选项 报错 strace trace system calls and signals ltrace A library call tracer 安装 首次使用可能出现这个提示 就是没安装 yum y install
  • ubuntu 远程服务器文件与本地文件互传

    放在这里自学 cite https blog csdn net Iv zzy article details 109412198 1 从服务器下载文件到本地 scp r 远程服务器用户名 远程IP 需要下载的文件路径 本地存放文件路径 2
  • 金融分析与风险管理——资本资产定价模型

    金融分析与风险管理 资本资产定价模型 1 系统性风险与非系统性风险 2 资本资产定价模型 1 系统性风险与非系统性风险 在理论上 股票面临的风险可以抽象的划分为系统性风险与非系统性风险 系统性风险 不可分散风险 也称市场风险 通常是由于公司
  • 数据挖掘十大算法(一):决策树算法 python和sklearn实现

    学完到第三章 决策树 python代码实现的仅是ID3算法 sklearn为优化过的C4 5 这里做一个详细的总结包括 原理 代码 可视化 scikit learn实现 皆为亲自实践后的感悟 以下进入正文 早前简单了解了决策树的原理 然后为
  • clang与gcc的区别

    gcc g 和 clang clang 都是Linux操作系统中常用的C C 编译器 gcc g 是gnu标准下的Linux的编译器 clang是一个C 编写 基于LLVM Low Level Virtual Machine 底层虚拟机 是
  • QT 设置程序图标

    在qt应用程序中 我们能想到的图标一般就以下三种 icon种类 应用窗口左上角的 图标 setWindowIcon 状态栏上显示的图标 setWindowIcon 可执行程序本身的图标 两种设置方式 下面会讲 对于以上三种图标都该怎么样设置
  • 【PTA】判断一个数是否为回文数

    1 题目 如果一个数与它的反转数相等 则该数为回文数 输入一个数 判断是否为回文数 输入格式 输入一个数 输出格式 若XX是回文数 则输出 XX 是回文数 若不是 则输出 XX 不是回文数 输入样例1 6234326 输出样例1 62343
  • radius认证服务器系统,03-Radius认证配置举例

    RADIUS 是一种用于在需要认证其链接的网络访问服务器 NAS 和共享认证服务器之间进行认证 授权和记帐信息的文档协议 RADIUS在运维审计系统中 主要体现的是认证功能 收集RADIUS服务器信息 IP地址 RADIUS端口号 RADI
  • 快速理解python中的可迭代对象、迭代器、生成器

    快速学会python中的可迭代对象 迭代器 生成器 今天周一 又要开始新的一周的学习了 加油鸭 多学一点不吃亏哈哈 迭代 什么是迭代 迭代就是将重复的代码进行执行 处理相似的数据集 并且上一个迭代的结果会给到下一次 作为初始化值进行下一次的
  • 利用Jmeter对禅道进行性能测试

    前言 jmeter工具的学习和使用 一 背景 前段时间没事 自己在虚拟机上搭建了一个禅道环境 并在外机上安装jmeter 编写脚本对禅道进行简单的性能测试 二 操作步骤 1 环境搭建 1 1 禅道环境搭建 本地电脑安装VMware Work
  • win11系统23年6月最新anaconda安装教程及配置环境变量

    win11系统23年6月最新anaconda安装教程及配置环境变量 官网下载anaconda 安装anaconda 注意 接下来这一步如果选择Just Me 后续则可选择自动 手动配置环境变量 如果选择ALL Users 则只能手动配置环境
  • 大模型AI:城市规划新的技术转换点

    引言 城市规划作为一项事业长期以来处在这样一种矛盾的境地 一方面干预的对象几乎是世界上最复杂的系统 城市系统 而另一方面城市规划和公共政策的强绑定属性 以及处在技术应用下游的定位使得行业并不对技术敏感 这就导致从业者往往采用传统的方法来解决
  • 复习-单项链表

    SList h pragma once typedef int SDataType typedef struct SListNode SDataType data struct SListNode PNext Node PNode 链表的结
  • 误删/usr/lib/x86_64-linux-gnu动态库文件修复

    问题 在阿里云上部署区块链时 因为缺少一些动态库 想偷懒从另外一个服务器上直接copy动态库 usr lib x86 64 linux gnu 结果先删除本地的 usr lib x86 64 linux gnu 结果造成好多命令用不了 例如
  • 为什么祖传代码被称为「屎山」?这个回答简直太形象了

    经常听说祖传代码会被人称之为 屎山 不同人可能有不同的体会 最近看到一个回答 简直是把这个阐述得 活灵活现 大家来感受下吧 阅读本文大概需要 3 分钟 作者 Avalon 原文链接 https www zhihu com question
  • win10时间 服务器未响应,win10系统程序未响应的解决方法

    win10系统想必大家都非常熟悉吧 然而有时候可能会碰到win10系统win10系统程序未响应的问题 比如近日有用户到本站反映说win10系统程序未响应的问题 该怎么处理win10系统程序未响应的问题呢 我们按照 1 在win10桌面右下角
  • 【二十六】springboot实现多线程事务处理

    springboot篇章整体栏目 一 springboot整合swagger 超详细 二 springboot整合swagger 自定义 超详细 三 springboot整合token 超详细 四 springboot整合mybatis p