java初学者指南_企业Java中事务隔离级别的初学者指南

java初学者指南

介绍

基于ACID事务属性的关系数据库强一致性模型。 在本文中,我们将阐明对资源本地事务和JTA事务使用不同的事务隔离级别和各种配置模式的背后原因。

隔离和一致性

在关系数据库系统中,原子性和持久性是严格的属性,而一致性和隔离性或多或少是可配置的。 我们甚至无法将一致性与隔离性分开,因为这两个属性始终是相关的。

隔离级别越低,系统获得的一致性越差。 从最小到最一致,有四个隔离级别:

  • 读未提交
  • READ COMMITTED(防止脏读)
  • 可重复读取(防止脏和不可重复读取)
  • 可序列化(防止脏的,不可重复的读取和幻像读取)

尽管最一致的SERIALIZABLE隔离级别是最安全的选择,但是大多数数据库默认改为READ COMMITTED。 根据阿姆达尔定律 ,为了容纳更多的并发事务,我们必须减少数据处理的串行部分。 锁获取间隔越短,数据库可以处理的请求越多。

隔离度

如前所述, 应用程序级可重复读取与乐观锁定机制配合使用非常方便,可以防止长时间对话中丢失更新 。

在高度并发的环境中,乐观锁定可能会导致高事务失败率。 与其他任何排队机制一样,悲观锁定在提供足够的锁定获取时间间隔时可能会容纳更多事务。

数据库和隔离级别

除MySQL(使用REPEATABLE_READ)外,大多数关系数据库系统的默认隔离级别为READ_COMMITTED。 所有数据库都允许您设置默认的事务隔离级别。

通常,数据库在多个应用程序之间共享,并且每个应用程序都有其自己的特定交易要求。 对于大多数事务,READ_COMMITTED隔离级别是最佳选择,我们仅应针对特定业务案例覆盖它。

这种策略被证明是非常有效的,它使我们对所有SQL事务的子集具有更严格的隔离级别。

数据源隔离级别

JDBC Connection对象允许我们为在该特定连接上发出的所有事务设置隔离级别。 建立新的数据库连接是一个资源消耗过程,因此大多数应用程序都使用连接池 DataSource 。 连接池数据源还可以设置默认的事务隔离级别:

  • DBCP
  • DBCP2
  • 光ikaCP
  • 骨CP
  • Bitronix交易管理器

与全局数据库隔离级别设置相比,DataSource级别的事务隔离配置更加方便。 每个应用程序可以设置自己的特定并发控制级别。

我们甚至可以定义多个数据源,每个数据源都有一个定义的隔离级别。 这样,我们可以动态地选择特定的隔离级别的JDBC连接。

Hibernate隔离级别

因为它必须同时支持资源本地事务和JTA事务,所以Hibernate提供了一种非常灵活的连接提供程序机制。

JTA事务需要XAConnection,并且JTA事务管理器负责提供XA兼容连接。

资源本地事务可以使用资源本地 DataSource ,对于这种情况,Hibernate提供了多个连接提供程序选项:

  • 驱动程序管理器连接提供程序(不合并连接,因此仅用于简单的测试方案)
  • C3P0连接提供程序(委派连接以获取对内部C3P0连接池数据源的调用)
  • 数据源连接提供程序(委派连接以获取对外部数据源的调用。

Hibernate提供了一个名为hibernate.connection.isolation的事务隔离级别配置,因此我们将检查所有上述连接提供者在获得此特定设置后的行为。

为此,我们将要:

  1. 创建一个SessionFactory:
    @Override
    protected SessionFactory newSessionFactory() {Properties properties = getProperties();return new Configuration().addProperties(properties).addAnnotatedClass(SecurityId.class).buildSessionFactory(new StandardServiceRegistryBuilder().applySettings(properties).build());
    }
  2. 打开一个新的会话并测试关联的连接事务隔离级别:
    @Testpublic void test() {Session session = null;Transaction txn = null;try {session = getSessionFactory().openSession();txn = session.beginTransaction();session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));}});txn.commit();} catch (RuntimeException e) {if ( txn != null && txn.isActive() ) txn.rollback();throw e;} finally {if (session != null) {session.close();}}}

唯一不同的是连接提供程序配置。

驱动程序管理器连接提供程序

驱动程序管理器连接提供程序为配置的数据库驱动程序提供了基本的DataSource包装器。 您仅应将其用于测试方案,因为它不提供专业的连接池机制。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}

测试生成以下输出:

WARN  [main]: o.h.e.j.c.i.DriverManagerConnectionProviderImpl - HHH000402: Using Hibernate built-in connection pool (not for production use!)
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationDriverConnectionProviderTest - Transaction isolation level is SERIALIZABLE

Hibernate会话关联的JDBC连接正在使用SERIALIZABLE事务隔离级别,因此hibernate.connection.isolation配置适用于此特定的连接提供程序。

C3P0连接提供者

Hibernate还提供了一个内置的C3P0连接提供程序。 像前面的示例一样,我们只需要提供驱动程序配置设置,然后Hibernate代表我们实例化C3P0连接池。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");properties.put("hibernate.show_sql", "true");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//c3p0 settingsproperties.put("hibernate.c3p0.min_size", 1);properties.put("hibernate.c3p0.max_size", 5);//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}

测试生成以下输出:

Dec 19, 2014 11:02:56 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
Dec 19, 2014 11:02:56 PM com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.2.1 [built 20-March-2013 10:47:27 +0000; debug? true; trace: 10]
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationInternalC3P0ConnectionProviderTest - Transaction isolation level is SERIALIZABLE

因此, hibernate.connection.isolation配置也适用于内部C3P0连接提供程序。

数据源连接提供程序

Hibernate不会强迫您使用特定的连接提供程序机制。 您只需提供一个DataSource,Hibernate就会在请求新的Connection时使用它。 这次,我们将创建一个成熟的DataSource对象,并将其传递给hibernate.connection.datasource配置。

@Override
protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");//data source settingsproperties.put("hibernate.connection.datasource", newDataSource());//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties;
}protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource;
}

测试生成以下输出:

DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceConnectionProviderTest - Transaction isolation level is READ_COMMITTED

这次似乎没有考虑hibernate.connection.isolation 。 Hibernate不会覆盖外部数据源,因此此设置在这种情况下是无用的。

如果使用外部数据源(例如,通过JNDI),则需要在外部数据源级别设置事务隔离。

要修复前面的示例,我们只需将外部数据源配置为使用特定的隔离级别:

protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");Properties properties = new Properties();properties.setProperty("hsqldb.tx_level", "SERIALIZABLE");actualDataSource.setProperties(properties);ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource;
}

生成以下输出:

DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceExternalconfgiurationConnectionProviderTest - Transaction isolation level is SERIALIZABLE

Java Enterprise事务隔离支持

Hibernate具有一个内置的Transaction API抽象层 ,将数据访问层与事务管理拓扑( 资源本地或JTA)隔离开。 虽然我们只能使用Hibernate事务抽象来开发应用程序,但将这种责任委托给中间件技术( JEE或Spring )更为常见。

Java企业版

JTA(Java事务API规范)定义了应如何由符合JEE的应用服务器管理事务。 在客户端,我们可以使用TransactionAttribute批注来划分事务边界。 虽然我们可以选择正确的事务传播设置,但是对于隔离级别我们不能做同样的事情。

JTA不支持事务范围的隔离级别,因此我们必须诉诸于供应商特定的配置才能为XA DataSource提供特定的事务隔离设置。

弹簧

Spring @Transactional批注用于定义事务边界。 与JEE相对,此批注允许我们配置:

  • 隔离级别
  • 异常类型回滚策略
  • 传播
  • 只读
  • 超时

正如我将在本文后面演示的那样,隔离级别设置仅可用于资源本地事务。 由于JTA不支持事务范围的隔离级别,因此Spring提供了IsolationLevelDataSourceRouter来克服使用应用程序服务器JTA DataSources时的这一缺点。

因为大多数DataSource实现只能采用默认的事务隔离级别,所以我们可以有多个这样的DataSource,每个为特定的事务隔离级别提供连接。

逻辑事务(例如@Transactional )隔离级别设置由IsolationLevelDataSourceRouter自省,因此将连接获取请求委托给可以为JDBC连接提供相同事务隔离级别设置的特定DataSource实现。

因此,即使在JTA环境中,事务隔离路由器也可以提供独立于供应商的解决方案,以基于每个事务覆盖默认数据库隔离级别。

Spring事务范围的隔离级别

接下来,我将测试Spring事务管理对资源本地事务和JTA事务的支持。

为此,我将介绍一个事务性业务逻辑服务Bean:

@Service
public class StoreServiceImpl implements StoreService {protected final Logger LOGGER = LoggerFactory.getLogger(getClass());@PersistenceContext(unitName = "persistenceUnit")private EntityManager entityManager;@Override@Transactional(isolation = Isolation.SERIALIZABLE)public void purchase(Long productId) {        Session session = (Session) entityManager.getDelegate();session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));}});}
}

Spring框架提供了一个事务管理抽象,它将应用程序逻辑代码与基础事务特定的配置分离。 Spring事务管理器只是实际资源本地或JTA事务管理器的基础。

本地资源到XA事务的迁移只是一个配置细节,而实际的业务逻辑代码则保持不变。 如果没有额外的事务管理抽象层和跨领域AOP支持,这将是不可能的。

接下来,我们将测试各种特定的事务管理器如何支持事务范围隔离级别覆盖。

JPA交易经理

首先,我们将测试JPA事务管理器:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory" /></bean>

当调用我们的业务逻辑服务时,这是我们得到的:

DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is SERIALIZABLE

JPA事务管理器只能使用一个数据源,因此它只能发出资源本地事务。 在这种情况下,Spring事务管理器能够覆盖默认的DataSource隔离级别(在本例中为READ COMMITTED)。

JTA交易经理

现在,让我们看看切换到JTA事务时会发生什么。 如前所述,Spring仅提供逻辑事务管理器,这意味着我们还必须提供物理JTA事务管理器。

传统上,企业应用服务器(例如Wildfly和WebLogic )负责提供符合JTA的事务管理器。 如今,还有各种各样的独立JTA事务管理器:

  • 比特龙
  • Atomikos
  • 红帽纳拉亚纳(RedHat Narayana)

在此测试中,我们将使用Bitronix:

<bean id="jtaTransactionManager" factory-method="getTransactionManager"class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"destroy-method="shutdown"/><bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/>
</bean>

运行先前的测试时,我们得到以下异常:

org.springframework.transaction.InvalidIsolationLevelException: JtaTransactionManager does not support custom isolation levels by default - switch 'allowCustomIsolationLevels' to 'true'

因此,让我们启用自定义隔离级别设置并重新运行测试:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/><property name="allowCustomIsolationLevels" value="true"/>
</bean>

该测试为我们提供了以下输出:

DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is READ_COMMITTED

即使有了这种额外的配置,事务范围的隔离级别也不会传播到基础数据库连接,因为这是JTA事务管理器的默认行为。

对于WebLogic,Spring提供了WebLogicJtaTransactionManager来解决此限制,正如我们在以下Spring源代码片段中所看到的:

// Specify isolation level, if any, through corresponding WebLogic transaction property.
if (this.weblogicTransactionManagerAvailable) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {try {Transaction tx = getTransactionManager().getTransaction();Integer isolationLevel = definition.getIsolationLevel();/*weblogic.transaction.Transaction wtx = (weblogic.transaction.Transaction) tx;wtx.setProperty(ISOLATION_LEVEL_KEY, isolationLevel);*/this.setPropertyMethod.invoke(tx, ISOLATION_LEVEL_KEY, isolationLevel);}catch (InvocationTargetException ex) {throw new TransactionSystemException("WebLogic's Transaction.setProperty(String, Serializable) method failed", ex.getTargetException());}catch (Exception ex) {throw new TransactionSystemException("Could not invoke WebLogic's Transaction.setProperty(String, Serializable) method", ex);}}
}
else {applyIsolationLevel(txObject, definition.getIsolationLevel());
}

结论

事务管理绝对不是一件容易的事,并且有了所有可用的框架和抽象层,它确实变得比人们想象的要复杂。

因为数据完整性对于大多数业务应用程序非常重要,所以唯一的选择是掌握当前的项目数据层框架堆栈。

  • Hibernate和JPA可用的代码。

翻译自: https://www.javacodegeeks.com/2014/12/a-beginners-guide-to-transaction-isolation-levels-in-enterprise-java.html

java初学者指南

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

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

相关文章

C语言内存/指针相关

内存数据类型–更好的内存管理数据类型&#xff1a;固定大小内存的别名typedef结构体的别名 typedef struct Student MyStudent给指针起别名 typedef char * PCHAR给变量起别名 typedef longlong mylong_t注意&#xff1a;void类型不能typedefvoid只能对函数返回和参数的限定vo…

c程序怎么改为java程序_如何将Java程序的入口点更改为C签名?

我在JNA试图在Java程序中执行一些C代码时愚弄.这是我在网上找到的一个工作示例(构建路径中需要JNA)&#xff1a;package core;import com.sun.jna.Library;import com.sun.jna.Native;import com.sun.jna.Platform;public class CoreController {public interface CLibrary ext…

求杨辉三角的前n行数据_LeetCode算法第118题:杨辉三角

题目描述&#xff1a;给定一个非负整数 numRows&#xff0c;生成杨辉三角的前 numRows 行。在杨辉三角中&#xff0c;每个数是它左上方和右上方的数的和。示例:输入: 5输出:[ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]]思路&#xff1a;杨辉三角形有如下两个特性每一行的第…

jasmine单元测试_使用Jasmine,Spock和Nashorn测试JVM服务器端JavaScript

jasmine单元测试JavaScript使用不仅限于浏览器中的客户端代码或NodeJS支持的服务器端代码。 许多基于JVM的项目都将其用作内部脚本语言。 测试这种功能既不简单也不标准。 在本文中&#xff0c;我打算演示一种使用成熟的工具&#xff08;例如Jasmine &#xff0c; Spock和Nasho…

常用于单片机的接口适配器模式C语言实现

一般实现在我们做项目的时候&#xff0c;一般的实现&#xff0c;可能我们会这样写代码// FileName: test.c// 来源&#xff1a;公众号【技术让梦想更伟大】#include#include “ExternModule.h” int main(void){/*初始化*/ vAllInit(); while(1) { /*项目逻辑*/ vLo…

java判断线程是否死锁_c++多线程锁 Mutex  自动判断死锁

c多线程锁可以使用absl::Mutex std::mutex这两种&#xff0c;下面是demo代码。使用absl:Mutex的时候打印&#xff1a;[mutex.cc : 1338] RAW: Cycle:[mutex.cc : 1352] RAW: mutex0x683b68 stack: 0x438562 absl::DebugOnlyDeadlockCheck() 0x4387b2 absl::Mutex::Lock() 0x43…

ar面部识别_国内手机厂商AR布局报告

欢迎关注AIRX的B站官方账号&#xff1a;AIRX社区&#xff0c;我们会定期和一些ARVR、AI企业和高校合作直播&#xff0c;分享前沿ARVR、AI、Unity、Unreal技术和教程。招运营、UI设计志愿者进行时&#xff0c;我们期待您的加入&#xff5e;文章来源 &#xff1a;增强现实核心技术…

rest服务swagger_在Java EE 7上骑骆驼–带有Swagger文档的REST服务

rest服务swagger骆驼开箱即用。 Swagger集成就是其中之一。 不幸的是&#xff0c;大多数已经存在的功能都严重依赖于Spring。 但这并不能阻止我们在普通的Java EE 7应用程序中使用它们&#xff0c;因为有时它只是更轻量级的处理方式。 但是我不想再对此进行讨论。 相反&#xf…

知识贴!单片机C语言编程之.H文件与.C文件的关系

一、.H文件与.C文件的关系&#xff1a;迄今为止&#xff0c;写过的程序都是一些很简单的程序&#xff0c;从来没有想到要自己写.H文件&#xff0c;也不知道.H文件到底什么用&#xff0c;与.C文件什么关系。只是最近写键盘程序&#xff0c;参考别人的程序时&#xff0c;发现别人…

java stream 分组求和_Java stream List 求和、分组操作

Java stream List 求和、分组操作前言项目中经常会使用Stream操作一些集合数据&#xff0c;今天记录一下我经常使用的Stream操作求和操作public static void main(String[] args) {List users generateUserList();int reduce users.stream().mapToInt(x -> x.getSalary().…

vector怎么按字段查询顺序输出_7大查询匹配类函数,一次给你总结好

Excel数据处理中&#xff0c;经常用到各种函数&#xff0c;可以说函数是Excel必不可少的一部分&#xff0c;今天向大家介绍数据处理中的七个查询匹配函数。下面一一介绍各函数的具体用法。vlookup功能&#xff1a;搜索表区域首列满足条件的元素&#xff0c;确定待检索单元格在区…

hibernate连接泄露_泄漏抽象,或如何正确地与Hibernate绑定Oracle DATE

hibernate连接泄露我们最近发布了一篇文章&#xff0c;介绍如何在SQL / JDBC和jOOQ中正确绑定Oracle DATE类型 。 这篇文章在Reddit上颇受关注&#xff0c; Vlad Mihalcea对此发表了有趣的评论&#xff0c;他经常在他的博客上撰写有关Hibernate&#xff0c;JPA&#xff0c;事务…

信工干货||C语言中的运算符和表达式

C语言中的运算符和表达式1算术运算符及表达式&#xff08;1&#xff09;算术运算符包括&#xff1a; &#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;%。&#xff08;2&#xff09;*&#xff0c;/&#xff0c;%同级&#xff0c;比 &#xff0c;-高。&#xff08;3&#x…

java程会释放锁join_关于join() 是否会释放锁的一些思考

# 首先从一个很有意思的问题开始&#xff1a;- 问 &#xff1a; Thread 的join() 方法是否会释放锁&#xff1f;- 答&#xff1a; 会&#xff01;# 如果一切到这里就结束了&#xff0c;那可能也就没有这篇小记了&#xff0c;但是我的脑子却冒出了一些奇怪的想法&#xff1a;- 释…

java lambda::_书评:精通Lambda:多核世界中的Java编程

java lambda::从版本8开始&#xff0c;λ编程&#xff08;lambda编程&#xff09;终于在Java世界中引入。此功能将在很大程度上改变Java开发人员的编程方式以及针对样板代码的新“武器”。 Java 8通过引入新的Stream API&#xff0c;大部分已将函数式编程应用在Collections API…

3皮卡丘眨眼代码_活见久,皮卡丘居然是一门编程语言

我很荣幸地向你介绍皮卡神教的编程语言&#xff0c;这门语言专为皮神设计(认真脸)。为什么一定要学习这门语言呢——谁不想要只皮卡丘我问你&#xff1f;在当今的宝可梦就业环境中&#xff0c;大多数皮卡丘们都在残忍的宝可梦训练师手下过着顺从和被奴役的生活。他们经常被迫与…

C语言关系运算符详解

关系运算符在使用时&#xff0c;它的的两边都会有一个表达式&#xff0c;比如变量、数值、加减乘除运算等&#xff0c;关系运算符的作用就是判明这两个表达式的大小关系。注意&#xff0c;是判明大小关系&#xff0c;不是其他关系。C语言提供了以下关系运算符&#xff1a;关系运…

sizeof不是java关键字是_下列哪项不是Java语言的关键字。

下列哪项不是Java语言的关键字。A:instanceofB:gotoC:volatileD:sizeof正确答案:sizeof解析&#xff1a;下列哪项不是Java语言的关键字。A:instanceofB:gotoC:volatileD:sizeof相关问题&#xff1a;汉代公主的丧葬不包括&#xff1a;A:柏椁B:没有墓道C:百官送葬D:樟棺大学生人际…

jboss fuse 教程_IDC关于使用JBoss Fuse的商业价值的报告(与Apache Camel一起使用)

jboss fuse 教程这只是一篇博客文章&#xff0c;具有更多的商业性质&#xff0c;但是您不能一无所有。 实际上&#xff0c;这也是使Apache Camel保持活力并保持良好状态的原因&#xff0c;这还归功于其商业上的成功。 希望从JBoss Fuse之类的产品中寻找有关在商业上使用Apache…

python list存储对象_python List 对象

从源码中可以看到&#xff0c;PyListObject中存储数据用的是PyObject **ob_item&#xff0c; 是当作Vector类似的方法。Items must normally not be NULL&#xff0c;这句是说list中的元素从list中删除&#xff0c;并不会立即free&#xff0c; 而是放到list的缓存池中。这个和整…