Executors-四种创建线程的手段

news2025/7/19 6:25:36

1 Executors.newCachedThreadPool()

从构造方法可以看出,它创建了一个可缓存的线程池。当有新的任务提交时,有空闲线程则直接处理任务,没有空闲线程则创建新的线程处理任务,队列中不储存任务。线程池不对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。如果线程空闲时间超过了60秒就会被回收。在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统OOM。

package com.zs.thread;

public class TestVolatile {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 5; i++) {
            final int index = i;
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        SimpleDateFormat sdf = new SimpleDateFormat(
                                "HH:mm:ss");
                        System.out.println("运行时间: " +
                                sdf.format(new Date()) + " " + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        cachedThreadPool.shutdown();
    }
}

因为这种线程有新的任务提交,就会创建新的线程(线程池中没有空闲线程时),不需要等待,所以提交的5个任务的运行时间是一样的。
在这里插入图片描述

package com.thread.excutor;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class FourThreadPoolTest {
    public static void main(String[] args) {
        newCachedThreadPool();
    }

    /**
     *     public static ExecutorService newCachedThreadPool() {
     *         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
     *                                       60L, TimeUnit.SECONDS,
     *                                       new SynchronousQueue<Runnable>());
     *     }
     *
     *  这些池通常会提高执行许多短期异步任务的程序的性能。
     *
     *  SynchronousQueue<Runnable>():
     *     1、SynchronousQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的。
     *     2、SynchronousQueue 是一个没有数据缓冲的BlockingQueue,容量为0,它不会为队列中元素维护存储空间,它只是多个线程之间数据交换的媒介。
     *     生产者线程对其的插入操作put必须等待消费者的移除操作take。
     *     3、SynchronousQueue非常适合传递性场景做交换工作,生产者的线程和消费者的线程同步传递某些信息、事件或者任务。
     *       SynchronousQueue的一个使用场景是在线程池里。如果我们不确定来自生产者请求数量,但是这些请求需要很快的处理掉,
     *       那么配合SynchronousQueue为每个生产者请求分配一个消费线程是处理效率最高的办法。
     *       Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,
     *       如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。
     *     4、SynchronousQueue 最大的特点在于,它的容量为0,没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入;
     *       同理,每次放数据的时候也会阻塞,直到有消费者来取。
     *     5、SynchronousQueue 的容量不是 1 而是 0,因为 SynchronousQueue 不需要去持有元素,它所做的就是直接传递(direct handoff)。
     *       由于每当需要传递的时候,SynchronousQueue 会把元素直接从生产者传给消费者,在此期间并不需要做存储,所以如果运用得当,它的效率是很高的。
     *       使用的数据结构是链表。使用CAS+自旋(无锁),自旋了一定次数后调用 LockSupport.park()进行阻塞。
     *
     *
     */
    private static void newCachedThreadPool() {
        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        System.out.println(executorService.getActiveCount());
        // sout -> 0
        IntStream.range(0, 5).boxed().forEach(item ->
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " [" + item + "]");
                    /**
                     * pool-1-thread-3 [2]
                     * pool-1-thread-4 [3]
                     * pool-1-thread-1 [0]
                     * pool-1-thread-5 [4]
                     * pool-1-thread-2 [1]
                     */
                }));
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(executorService.getActiveCount()); // sout -> 5
    }
    /**
     * 60s之后线程池停止
     * 0
     * 5
     * pool-1-thread-3 [2]
     * pool-1-thread-4 [3]
     * pool-1-thread-1 [0]
     * pool-1-thread-5 [4]
     * pool-1-thread-2 [1]
     *
     * Process finished with exit code 0
     */
}

2 Executors.newFixedThreadPool(10)

从构造方法可以看出,它创建了一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程数达到线程池的最大值nThreads。线程池的大小一旦达到最大值后,再有新的任务提交时则放入阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。FixedThreadPool提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

package com.zs.thread;
public class TestVolatile {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
                        System.out.println("运行时间: " + sdf.format(new Date()) + " " + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        fixedThreadPool.shutdown();
    }
}

例中创建了一个固定大小为3的线程池,然后在线程池提交了5个任务。在提交第4个任务时,因为线程池的大小已经达到了3并且前3个任务在运行中,所以第4个任务被放入了队列,等待有空闲的线程时再被运行。运行结果如下(注意前3个任务和后2个任务的运行时间):
在这里插入图片描述

/**
     *     public static ExecutorService newFixedThreadPool(int nThreads) {
     *         return new ThreadPoolExecutor(nThreads, nThreads,
     *                                       0L, TimeUnit.MILLISECONDS,
     *                                       // Creates a LinkedBlockingQueue with a capacity of Integer.MAX_VALUE.
     *                                       new LinkedBlockingQueue<Runnable>());
     *     }
     */
    private static void newFixedThreadPool() {
        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        IntStream.range(0, 20).boxed().forEach(item ->
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " [" + item + "]");
                }));
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(executorService.getActiveCount());
    }
10
pool-1-thread-10 [9]
pool-1-thread-1 [0]
pool-1-thread-4 [3]
pool-1-thread-5 [4]
pool-1-thread-9 [8]
pool-1-thread-6 [5]
pool-1-thread-3 [2]
pool-1-thread-8 [7]
pool-1-thread-2 [1]
pool-1-thread-7 [6]
10s之后。。。
pool-1-thread-9 [12]
pool-1-thread-4 [11]
pool-1-thread-6 [13]
pool-1-thread-7 [17]
pool-1-thread-5 [18]
pool-1-thread-2 [16]
pool-1-thread-8 [15]
pool-1-thread-1 [10]
pool-1-thread-3 [14]
pool-1-thread-10 [19]

程序不会停止。。。

3 Executors.newSingleThreadExecutor()

从构造方法可以看出,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

package com.zs.thread;

public class TestVolatile {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 5; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        SimpleDateFormat sdf = new SimpleDateFormat(
                                "HH:mm:ss");
                        System.out.println("运行时间: " +
                                sdf.format(new Date()) + " " + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        singleThreadExecutor.shutdown();
    }
}

因为该线程池类似于单线程执行,所以先执行完前一个任务后,再顺序执行下一个任务:
在这里插入图片描述
既然类似于单线程执行,那么这种线程池还有存在的必要吗?这里的单线程执行指的是线程池内部,从线程池外的角度看,主线程在提交任务到线程池时并没有阻塞,仍然是异步的。

/**
     *     public static ExecutorService newSingleThreadExecutor() {
     *         return new FinalizableDelegatedExecutorService
     *             (new ThreadPoolExecutor(1, 1,
     *                                     0L, TimeUnit.MILLISECONDS,
     *                                     new LinkedBlockingQueue<Runnable>()));
     *     }
     *
     *
     */
    private static void newSingleThreadExecutor() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        IntStream.range(0, 8).boxed().forEach(item ->
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " [" + item + "]" + System.currentTimeMillis());
                }));
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
pool-1-thread-1 [0]1669445965474
pool-1-thread-1 [1]1669445967480
pool-1-thread-1 [2]1669445969481
pool-1-thread-1 [3]1669445971487
pool-1-thread-1 [4]1669445973492
pool-1-thread-1 [5]1669445975497
pool-1-thread-1 [6]1669445977500
pool-1-thread-1 [7]1669445979505

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

不会停止运行,始终有一个线程在运行。

4 Executors.newScheduledThreadPool()

这个方法创建了一个固定大小的线程池,支持定时及周期性任务执行。
首先看一下定时执行的例子:

package com.zs.thread;

public class TestVolatile {
    public static void main(String[] args) {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        System.out.println("提交时间: " + sdf.format(new Date()));
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("运行时间: " + sdf.format(new Date()));
            }
        }, 3, TimeUnit.SECONDS);
        scheduledThreadPool.shutdown();
    }
}

使用该线程池的schedule方法,延迟3秒钟后执行任务,运行结果如下:
在这里插入图片描述

package com.zs.thread;

public class TestVolatile {
    public static void main(String[] args) throws InterruptedException {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        System.out.println("提交时间: " + sdf.format(new Date()));
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("运行时间: " + sdf.format(new Date()));
            }
        }, 1, 3, TimeUnit.SECONDS);
        Thread.sleep(10000);
        scheduledThreadPool.shutdown();
    }
}

使用该线程池的scheduleAtFixedRate方法,延迟1秒钟后每隔3秒执行一次任务,运行结果如下:
在这里插入图片描述

5 Executors.newWorkStealingPool()

 /**
     *     public static ExecutorService newWorkStealingPool() {
     *         return new ForkJoinPool
     *             (Runtime.getRuntime().availableProcessors(),
     *              ForkJoinPool.defaultForkJoinWorkerThreadFactory,
     *              null, true);
     *     }
     *
     *     可以传入线程的数量,不传入则默认使用当前计算机中可用的cpu数量,能够合理的使用CPU进行对任务操作(并行操作)。
     *     适合使用在很耗时的任务中,底层用的ForkJoinPool 来实现的:
     *     ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”分发到不同的cpu核心上执行,执行完后再把结果收集到一起返回。
     */
    private static void newWorkStealingPool() {
        /*Optional.of(Runtime.getRuntime().availableProcessors())
                .ifPresent(System.out::println);*/ // 8核CPU
        SimpleDateFormat sdf = new SimpleDateFormat(
                "HH:mm:ss");
        ExecutorService executorService = Executors.newWorkStealingPool();
        List<Callable<String>> callableList = IntStream.range(0, 20).boxed().map(item ->
                (Callable<String>) () -> {
                    System.out.println("Thread - " + Thread.currentThread().getName() + " - " + sdf.format(new Date()));
                    sleep(2L);
                    return "task-" + item;
                }).collect(Collectors.toList());
        try {
          // invokeAll的作用是:等待所有的任务执行完成后统一返回。
          // 这里与大家分享的是:如果executorService是公共线程池慎用,如果这时候有另外一个请求也不断地往线程池里扔任务,这时候这个请求是不是就一直不停的阻塞了。
            executorService.invokeAll(callableList).stream().map(item -> {
                try {
                    return item.get();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }).forEach(System.out::println);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

线程会执行结束!

Thread - ForkJoinPool-1-worker-3 - 15:29:15
Thread - ForkJoinPool-1-worker-1 - 15:29:15
Thread - ForkJoinPool-1-worker-6 - 15:29:15
Thread - ForkJoinPool-1-worker-2 - 15:29:15
Thread - ForkJoinPool-1-worker-4 - 15:29:15
Thread - ForkJoinPool-1-worker-5 - 15:29:15
Thread - ForkJoinPool-1-worker-0 - 15:29:15
Thread - ForkJoinPool-1-worker-7 - 15:29:15
Thread - ForkJoinPool-1-worker-3 - 15:29:17
Thread - ForkJoinPool-1-worker-4 - 15:29:17
Thread - ForkJoinPool-1-worker-0 - 15:29:17
Thread - ForkJoinPool-1-worker-5 - 15:29:17
Thread - ForkJoinPool-1-worker-6 - 15:29:17
Thread - ForkJoinPool-1-worker-7 - 15:29:17
Thread - ForkJoinPool-1-worker-2 - 15:29:17
Thread - ForkJoinPool-1-worker-1 - 15:29:17
Thread - ForkJoinPool-1-worker-0 - 15:29:19
Thread - ForkJoinPool-1-worker-4 - 15:29:19
Thread - ForkJoinPool-1-worker-5 - 15:29:19
Thread - ForkJoinPool-1-worker-1 - 15:29:19
task-0
task-1
task-2
task-3
task-4
task-5
task-6
task-7
task-8
task-9
task-10
task-11
task-12
task-13
task-14
task-15
task-16
task-17
task-18
task-19

Process finished with exit code 0

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESP三相SVPWM控制器的simulink仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB程序 1.算法描述 SVPWM则以三相的合成矢量为出发点&#xff0c;其基本思想为&#xff1a;在数学意义上的abc轴也好&#xff0c;αβ轴也好&#xff0c;其产生的电压都应该等于dq轴合成的那个电压。那么只要让…

swiper在动态创建dom过程中的问题:数据从后端请求回来后加载到页面上,dom加载完发现swiper没用了

怎么动态创建div标签&#xff1a; 要轮播的数据是后端返回的&#xff0c;所以我们要发ajax请求接收数据&#xff1b; 下面演示的是已经接收回来的数据&#xff0c;动态创建div标签&#xff1a; setTimeout(()>{var list ["aaa","bbb","ccc&quo…

【Redis】从计算机组成原理开始讲解为什么会出现Redis

文章目录前置知识数据库的出现Redismemcache与redis的区别前置知识 首先需要知道的一个常识就是&#xff1a;数据是存放在磁盘里面的。 而磁盘有两个指标&#xff1a; 寻址&#xff1a;表示找到对应的数据所需要的时间&#xff0c;ms带宽&#xff1a;表示单位时间可以有多少个…

Python排序:冒泡排序,选择排序,插入排序,希尔排序

编程中的交换元素逻辑&#xff1a; # python中交换元素 有内置的三方底层逻辑 可以直接交换 a 2 b 3 a, b b, a print(a) # a为3# 其他编程需要有一个中间的变量来转换 变量设为temp a 2 b 3 temp a a b b temp print(a) # a为3 -----冒泡排序----- 相邻…

openfeign原理

openfeign原理 EnableFeignClients注解启用Feign客户端&#xff0c;通过Import注解导入了FeignClientsRegistrar类加载额外的Bean。FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口&#xff0c;在Spring启动过程中会调用registerBeanDefinitions方法注册BeanDe…

自动化项目倍加福WCS-PG210E使用GSD文件

1&#xff0e;硬件电气连接 WCS-PG210E WCS3B WCS2B Pin 颜色 Pin 颜色 24V UB 1 BN棕色 2 WH白色 0V GND 3 BU蓝色 3 BU蓝色 RS485- RS485- 4 BK黑色 1 BN棕色 RS485 RS485 2 WH白色 4 BK黑色 保留 5 GY灰色 5 GY灰色 2. 安装W…

Nginx (4):nginx动静分离

什么是动静分离不解释了&#xff0c;网上说的很清楚&#xff0c;这里只说配置 目的 02虚拟机运行一个tomcat&#xff0c;处理动态请求&#xff0c;而对静态文件的访问则交给01虚拟机。操作 下面是01虚拟机的配置文件内容&#xff1a; server {listen 82;listen [::]:82;#root /…

六、nacos环境隔离、服务配置拉取和多环境配置共享

文章目录一、环境隔离-namespace1.namespace理解2.创建命名空间二、Nacos-实现配置管理三、nacos-实现服务配置拉取1.非热更新2.热更新&#xff1a;四、实现多环境配置共享1.开发环境&#xff1a;2.测试环境3.结论一、环境隔离-namespace 1.namespace理解 Nacos中服务存储和数…

Element Plus 组件库相关技术:7. 组件实现的基本流程及 Icon 组件的实现

前言 本章节我们将要实现 Icon 组件&#xff0c;Icon 组件应该是所有组件里面最简单的一个组件了&#xff0c;所以我们由简入深&#xff0c;循序渐进进行学习。Icon 组件虽然简单&#xff0c;但它却包含了一个组件的全部基础流程&#xff0c;通过实现 Icon 组件进行理解 Eleme…

疫情失业之下,测试的未来在哪里

前天和测试圈子里一个朋友聊了关于今年求职招聘市场行情和个人认知以及发展副业的话题。 聊起了今年的求职招聘行情&#xff0c;他说他们公司已经裁了一波人了&#xff0c;估计年底还会有一波裁员。 今年的市场冷的有点吓人&#xff0c;在这么下去&#xff0c;他也会担心自己…

nacos实现负载均衡、权重

文章目录一、nacos服务分级存储模型二、Nacos-NacosRule 实现负载均衡三、nacos-服务实例的权重设置一、nacos服务分级存储模型 修改 application.yml 配置文件&#xff1a; spring:cloud:nacos:server-addr: localhost:8848discovery:cluster-name: HZ #集群位置&#xff0c…

Linux C/C++ 学习笔记(九):百万并发的服务器实现

本文内容参考自(2条消息) Linux C/C 开发&#xff08;学习笔记十三)&#xff1a;百万并发的服务器实现_菊头蝙蝠的博客-CSDN博客_linux百万并发 一、connection_refuesed ---->文件系统最大的进程fd个数 nat 模式&#xff0c;物理机的VMnet8网卡&#xff0c;连接到了VMnet…

selenium--关闭窗口,指定窗口大小,前进,后退,刷新等等

关闭窗口跳转到指定页面窗口大小设置返回上个页面前进到下一个页面页面刷新关闭窗口 在selenium中执行完关闭窗口一般有两种方法&#xff1a; driver.close() driver.quit()这两个都是常用的方法&#xff0c;但是他们有什么区别呢&#xff1f; 对于driver.close(),他是关闭当…

【FME实战教程】003:FME读取地理空间数据(矢量、栅格、点云、三维模型、数据库、地理服务)大全

FME读取地理空间数据&#xff08;矢量、栅格、点云、三维模型、空间数据库、地理服务&#xff09;大全。 文章目录1. FME读取数据1.1 读取矢量1.1.1 读取Shapefile1.1.2 读取dwg1.2 读取栅格数据1.2.1 影像DOM1.3 读取地理数据库1.3.1 读取文件数据库&#xff08;.gdb&#xff…

机械原理复习试题

​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; ​ 编辑 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; ​ 编辑…

聚类分析的基本概念和方法

聚类分析的基本概念和方法 文章目录聚类分析的基本概念和方法前言一、什么是聚类分析1、聚类分析基本流程与步骤2、 什么是好的聚类方法3、聚类的模型评估4、聚类分析的比较5、聚类分析的挑战二、基本聚类方法概述三、划分算法1、基本概念2、k-means 聚类方法1、k-means 方法的…

CMake中configure_file的使用

CMake中的configure_file命令用于将一个文件拷贝到另一个位置并修改其内容&#xff0c;其格式如下&#xff1a; configure_file(<input> <output>[NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |FILE_PERMISSIONS <permissions>...][COPYONLY] [ESCAPE_…

01 一条SQL 语句是如何执行的?

select * from teacher where id 10 1、一条简单的sql语句底层的执行过程是怎么样的&#xff1f; 答&#xff1a;一条sql执行会经过连接器、查询缓存、分析器、优化器和执行器等步骤。 2、连接器的作用是什么&#xff1f; 答&#xff1a;sql查询&#xff0c;首先连接到这个数…

【机器学习项目实战10例】(四):利用XGBoost实现短期电力负荷预测

💥 项目专栏:【机器学习项目实战10例】 文章目录 一、利用XGBoost实现短期电力负荷预测二、数据集介绍三、将数据进行标准化四、形成训练数据五、划分训练集、测试集六、定义模型七、模型训练八、训练集、测试集验证九、网络搜索十、绘制结果一、利用XGBoost实现短期电力负荷…

分布式事务

一、事务 1.1、什么是事务&#xff1f; 事务&#xff08;transaction&#xff09;是访问并操作数据库中数据的一个程序执行单元&#xff0c;由开始事务和提交事务之间的所有的语句组成。事务的结束有两种&#xff0c;一个是事务中间的所有操作执行成功&#xff0c;提交事务。一…