【Java技术专题】「入门到精通系列教程」零基础带你认识网络请求工具鼻祖之HttpClient开发实战指南(执行请求篇)

news2024/10/5 17:28:40

零基础带你认识网络请求工具鼻祖之HttpClient开发实战指南

  • 前言
  • HttpClient的作用
    • HttpClient可以实现什么?
    • HttpClient无法实现什么?
  • HttpClient的使用基础
    • 执行请求
      • HTTP 请求
        • 查询字符串也可以从独立的参数中来生成
      • HTTP 响应
      • HTTP 实体
        • 实体类型
        • 重复实体
        • 使用 HTTP 实体
        • 处理获取的报文数据
        • 确保低级别资源释放
        • 消耗实体内容
      • 生成实体内容
        • 动态内容实体
        • 模拟提交HTML 表单
        • 内容分块
      • 响应控制器
  • 总体介绍

前言

HTTP 是目前互联网上最重要的协议之一。随着 Web 服务、网络设备以及网络计算的不断发展,HTTP 协议的作用不断扩大,不仅限于 Web 浏览器的使用范围,也增加了需要 HTTP 协议支持的应用程序的数量。虽然 java.net 包提供了基本的通过 HTTP 访问资源的功能,但是它缺乏全面的灵活性和其他许多应用程序需要的功能。因此,HttpClient 组件就是一款旨在填补这一空白的软件包,通过提供有效、保持更新且功能丰富的功能,实现客户端最新的 HTTP 标准和建议。HttpClient 组件旨在为扩展而设计,提供强大的支持,使开发人员能够构建基于 HTTP 客户端的应用程序,例如 Web 浏览器、Web 服务端,以及利用或扩展 HTTP 协议进行分布式通信的系统。

HttpClient的作用

HttpClient可以实现什么?

这个客户端 HTTP 运输实现库是基于 HttpCore(http://hc.apache.org/httpcomponents-core/index.html)开发的。它使用经典(阻塞)I/O 操作来实现 HTTP 传输。

HttpClient无法实现什么?

HttpClient 并非浏览器,而是一个客户端 HTTP 通讯实现库。它的主要目的是发送和接收 HTTP 报文。和浏览器不同的是,HttpClient 不会缓存内容、执行 HTML 页面中的 JavaScript 代码、猜测内容类型、重新格式化请求/重定向 URI 或实现和 HTTP 通讯无关的功能。

HttpClient的使用基础

执行请求的总体流程介绍:
在这里插入图片描述

执行请求

HttpClient 最主要的功能是执行 HTTP 方法,该过程包含一个或多个 HTTP 请求/响应的交互,通常由 HttpClient 内部处理。用户需要提供请求对象,而 HttpClient 则传输请求到目标服务器并返回对应的响应对象,或在执行失败时抛出异常。因此,HttpClient API 的关键在于定义描述上述规约的 HttpClient 接口。

下面是一个简单的请求执行示例:

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
	InputStream instream = entity.getContent();
	int l;
	byte[] tmp = new byte[2048];
	while ((l = instream.read(tmp)) != -1) {
	}
}

HTTP 请求

HttpGet httpget = new HttpGet("http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");

HttpClient 提供很多工具方法来简化创建和修改执行 URI。URI 也可以编程来拼装:

URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
"q=httpclient&btnG=Google+Search&aq=f&oq=", null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());

输出内容为:

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

查询字符串也可以从独立的参数中来生成

List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("q", "httpclient"));
qparams.add(new BasicNameValuePair("btnG", "Google Search"));
qparams.add(new BasicNameValuePair("aq", "f"));
qparams.add(new BasicNameValuePair("oq", null));
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
URLEncodedUtils.format(qparams, "UTF-8"), null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());

输出内容为:

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

HTTP 响应

HTTP 响应是由服务器在接收和解释请求报文之后返回发送给客户端的报文。响应报文的第一行包含了协议版本,之后是数字状态码和相关联的文本段。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());

输出内容为:

HTTP/1.1
200
OK
HTTP/1.1 200 OK

HTTP 报文头部信息可以描述许多内容,如内容长度、内容类型等。HttpClient 提供了方法来获取、添加、移除和枚举头部信息。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);

输出内容为:

Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"

如果要获得特定类型的所有头部信息,HeaderIterator 接口是最有效的方式。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie",
"c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie",
"c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}

输出内容为:

Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"

此接口还可用于将 HTTP 报文解析为各个独立的头部信息元素。

	HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");
	response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
	response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");
	HeaderElementIterator it = new BasicHeaderElementIterator(
	response.headerIterator("Set-Cookie"));
	while (it.hasNext()) {
		HeaderElement elem = it.nextElement();
		System.out.println(elem.getName() + " = " + elem.getValue());
		NameValuePair[] params = elem.getParameters();
		for (int i = 0; i < params.length; i++) {
			System.out.println(" " + params[i]);
		}
}
c1 = a
path=/
domain=localhost
c2 = b
path=/
c3 = c
domain=localhost

HTTP 实体

HTTP 报文能够携带与请求或响应相关的内容实体,但这些实体是可选的。在某些请求和响应中可能出现。使用了实体的请求被称为封闭实体请求,而HTTP规范定义了两种封装实体的方法:POST和PUT。响应通常会包含一个内容实体,但也有例外,例如 HEAD 方法的响应以及 204 No Content、304 Not Modified 和 205 Reset Content 响应。
在这里插入图片描述

实体类型

HttpClient根据实体的内容来源将其分为三种类型:
在这里插入图片描述

  • 流式实体:内容要么从流中获得,要么在运行时生成。特别是从 HTTP 响应中获取的实体属于此类。流式实体不可重复生成。

  • 自我包含式实体:内容在内存中或通过独立的连接或其他实体中获得。此类实体可以重复生成。这种类型的实体通常用于封闭 HTTP 请求的实体。

  • 包装式实体:内容从另一个实体中获得。

对于从 HTTP 响应中获取流式内容的情况,这种区分对于连接管理非常重要。但对于仅由 HttpClient 发送的请求实体,流式和自我包含式实体的不同就不那么重要了。在这种情况下,建议考虑使用不能重复生成的流式实体或者可以重复生成的自我包含式实体。

重复实体

如果一个实体是自我包含式的(例如 ByteArrayEntity 或 StringEntity),那么它的内容可以被多次读取,这就意味着这种实体可以重复使用。

使用 HTTP 实体

一个实体可以代表二进制内容或字符内容,因此支持字符编码(如果是字符内容)。实体是在执行请求时,或请求已成功执行时,或将响应体发送到客户端时创建的。要读取实体内容,可以通过HttpEntity#getContent()方法从输入流中获取内容。它将返回一个java.io.InputStream对象。您还可以通过调用HttpEntity#writeTo(OutputStream)方法,向给定的输出流写入实体中的内容,该方法将一次返回写入到流中的所有内容。

处理获取的报文数据

当使用一个收到的报文获取实体时,可使用HttpEntity#getContentType()方法和HttpEntity#getContentLength()方法读取常见的元数据,如Content-Type和Content-Length头信息(如果可用)。HttpEntity#getContentEncoding()方法用于读取Content-Type头信息中可能包含的文本MIME类型的字符编码,例如text/plain或text/html。如果头信息不可用,则长度返回-1,ContentType返回NULL。如果Content-Type头信息可用,则返回一个Header对象。

传出报文创建实体时,需要通过实体创建器提供元数据。

StringEntity myEntity = new StringEntity("important message","UTF-8");
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(myEntity));
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);

输出内容为

Content-Type: text/plain; charset=UTF-8
17
UTF-8
important message
17

确保低级别资源释放

完成响应实体后,务必完全消耗实体内容,以确保连接可以安全地返回到连接池中并由连接管理器重用。可以通过调用 HttpEntity#consumeContent()方法来方便地处理此操作。当 HttpClient 探测到内容流已经到达尾部时,会自动释放连接并返回到连接管理器。此外,多次调用 HttpEntity#consumeContent()方法也是安全的。

有时会出现特殊情况,例如只需要获取响应内容的一小部分,消耗剩余内容会导致性能损失,或者重用连接的代价太高。这种情况下,可以通过调用 HttpUriRequest#abort()方法来中止请求。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
httpget.abort();
}

虽然连接不会被重用,但仍然会正确释放由它持有的所有级别的资源。

消耗实体内容

推荐使用 HttpEntity#getContent() 或 HttpEntity#writeTo(OutputStream) 方法来消耗实体内容。HttpClient 还自带 EntityUtils 类,其中包含一些静态方法,可更轻松地从实体中读取内容或信息。可以使用该类方法以字符串/字节数组的形式获取整个内容体,而不是直接读取 java.io.InputStream。需要注意的是,强烈不建议使用 EntityUtils,除非响应实体源自可靠的 HTTP 服务器并有已知的长度限制。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
} else {
// Stream content out
}
}

在某些情况下,可能需要对实体内容进行多次读取。为此,需要使用某种方式将实体内容缓存到内存或磁盘中。最简单的方法是使用 BufferedHttpEntity 类来封装源实体,这样可以将源实体的内容读取到内存缓冲区中。在其他方式中,实体包装器会获得源实体的引用。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity = new BufferedHttpEntity(entity);
}

生成实体内容

HttpClient 提供了多种类来生成通过 HTTP 连接获得内容的有效输出流。这些类的实例可以和封闭如 POST 和 PUT 请求的实体相关联,以封闭实体从 HTTP 请求中获得的输出内容。HttpClient 还提供了多种公用的数据容器,如字符串、字节数组、输入流和文件等,来保证数据的完整性和传输的可靠性。这些数据容器的具体类包括:StringEntity、ByteArrayEntity、InputStreamEntity 和 FileEntity。
在这里插入图片描述

File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file, "text/plain; charset=\"UTF-8\"");
HttpPost httppost = new HttpPost("http://localhost/action.do");
httppost.setEntity(entity);

注意,InputStreamEntity 无法重复读取内容,因为它只能从底层数据流中读取一次。为了避免此问题,建议使用自定义的 HttpEntity 类,这是一种自包含的实现,可以替代使用通用的 InputStreamEntity。另外,FileEntity 也是一个很好的选择

动态内容实体

通常,HTTP实体需要基于特定的执行上下文来动态生成。HttpClient通过使用EntityTemplate实体类和ContentProducer接口提供了动态实体的支持。内容生成器是生成它们内容的对象,将它们写入到一个输出流中。它们是每次请求时按需生成内容,所以由EntityTemplate创建的实体通常是自包含的并且可以重复使用。

ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<response>");
writer.write(" <content>");
writer.write(" important stuff");
writer.write(" </content>");
writer.write("</response>");
writer.flush();
}
};
HttpEntity entity = new EntityTemplate(cp);
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);

模拟提交HTML 表单

许多应用程序经常需要模拟提交HTML表单的过程,以记录Web应用程序或提交输出数据。HttpClient提供了一个特殊的实体类UrlEncodedFormEntity,来支持这个过程。

List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("param1", "value1"));
formparams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);

使用UrlEncodedFormEntity实例时,会使用URL编码来编码参数,生成如下的内容: param1=value1&param2=value2

内容分块

通常情况下,我们建议使用HttpClient自行选择最适合的编码转换方式,这是可能的。而设置HttpEntity#setChunked()方法为true可以告知HttpClient使用分块编码。需要注意的是,HttpClient会将这个标识作为提示。当HTTP协议版本(如HTTP/1.0)不支持分块编码时,这个属性值会被忽略。

StringEntity entity = new StringEntity("important message","text/plain; charset=\"UTF-8\"");
entity.setChunked(true);
HttpPost httppost = new HttpPost("http://localhost/acrtion.do");
httppost.setEntity(entity);

响应控制器

使用ResponseHandler接口是最简单也是最方便控制响应的方式。使用该接口后,用户无需再担心连接管理的问题。HttpClient会自动管理连接,确保连接被释放到连接管理器中去,不管请求执行成功与否或引发了异常。

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
public byte[] handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
	HttpEntity entity = response.getEntity();
	if (entity != null) {
		return EntityUtils.toByteArray(entity);
	} else {
		return null;
	}
 }
};
byte[] response = httpclient.execute(httpget, handler);

总体介绍

此文介绍了HttpClient库中的几个重要概念和功能。

  • HttpClient最主要的功能是执行HTTP方法,通过HTTP请求对象生成响应对象,HttpClient负责连接服务器并返回响应,同时在执行失败时抛出异常。
  • HTTP响应是服务器在解析请求后返回的HTTP报文,包含协议版本、状态码和文本段。HTTP报文可以携带内容实体,HttpClient提供了多种类来生成通过HTTP连接获得内容的有效输出流,还提供了多种数据容器,如字符串、字节数组、输入流和文件等,用于保证数据的完整性和传输的可靠性。
  • 最简单、最方便控制响应的方式是使用ResponseHandler接口,通过使用该接口,用户无需再担心连接管理的问题,HttpClient会自动管理连接,并确保连接被释放到连接管理器中去,无论请求是否成功或引发了异常。

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

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

相关文章

重新审视长时间序列预测:关于线性映射的调查

摘要 近年来&#xff0c;长时间序列预测得到了极大的关注。虽然有各种专门的设计来捕捉时间依耐性&#xff0c;但以前的研究表明&#xff0c;与其他复杂的架构相比&#xff0c;单一的线性层可以实现有竞争力的预测性能。在本文中&#xff0c;我们彻底调查了最近提出的方法内在…

微信小程序开发21__Echarts的应用

Echarts 是一个使用JS实现的开源可视化库&#xff0c; 其官网是 https://echarts.apache.org . 它提供了常规的折线图、柱状图、散点图、饼图、K线图等&#xff0c; 还支持图与图之间的混搭。 Echarts 的微信小程序版本的Github为 https://github.com/ecomfe/echarts-for-…

MySQL数据库日志管理、备份与恢复

目录 一、MySQL 日志管理 二、数据备份的重要性 造成数据丢失的原因 三、数据库备份的分类 1 、从物理与逻辑的角度 &#xff08;1&#xff09;备份划分 &#xff08;2&#xff09; 物理备份方法 2、 从数据库的备份策略角度 四、常见的备份方法 1、物理冷备 2、专用备…

天气热了,三叔请你吃瓜

目录 前言预备知识画一个完整的西瓜加些纹路切西瓜参考资料前言 六月份转眼就过去一半了,气温逐渐走高,每次经过小区门口的时候都会被那家水果店铺的吆喝声吸引住,“正宗南汇8424西瓜只要3元一斤啦”,每每都想冲进店里抱一个回来,可碍于囊中羞涩,只好作罢,晚餐过后闲暇…

Postman | 一分钟掌握Pre-request Script | 外部库的使用

简介 Postman内部提供了13种外部库&#xff0c;可以直接在前置请求脚本和后置请求脚本中使用&#xff0c;如果需要其他外部库&#xff0c;只需要使用如下方式引入即可&#xff1a; require(moduleName:String):function → * 一些内部库使用的例子 postman可用的外部库官网列…

[第一章 web入门]afr

afr_1 题目开头已经提示说是任意文件读取漏洞 所以这里还需要复习一下php伪协议php://filter 的作用 读取源代码并进行base64编码输出&#xff0c;不然传入的参数会直接当做php代码执行就看不到源代码内容了。php://filter即使在allow_url_fopen和allow_url_include双off情况下…

Day21 实战篇——Jmeter接口测试之案例实战——添加线程组、添加HTTP请求、获取所有学生接口

Day21 实战篇——Jmeter接口测试之案例实战——添加线程组、添加HTTP请求、获取所有学生接口 文章目录 Day21 实战篇——Jmeter接口测试之案例实战——添加线程组、添加HTTP请求、获取所有学生接口1、添加线程组2、添加HTTP请求3、获取所有学生接口3.1 Jmeter察看结果树中乱码:…

03_运行时数据区

目录 一、概述二、线程1、JVM系统线程 一、概述 二、线程 1、JVM系统线程

调用万维易源API实现天气预测

目录 1. 作者介绍2. 关于理论方面的知识介绍2.1 天气预测简介2.2 预测方法 3. 代码实现3.1 需要安装的包3.2 部分代码3.3 实验结果 1. 作者介绍 房庚晨&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;22级研究生 研究方向&#xff1a;机器视觉与人工智能 电子…

【数据库四】MySQL备份与恢复

MySQL备份与恢复 1.数据库备份的分类1.1 数据备份的重要性1.2 数据库备份的分类1.3 常见的备份方法 2.MySQL完全备份与恢复2.1 MySQL完全备份2.2 数据库完全备份分类2.3 MySQL物理冷备份及恢复2.4 数据迁移DST2.5 mysqldump进行逻辑备份2.5.1 mysqldump备份数据库2.5.2 mysqldu…

基于卡尔曼滤波进行四旋翼动力学建模(SimulinkMatlab)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

下载routeview网站上的BGP更新数据

需求 给出起始时间和终止时间&#xff0c;从routeview网站上&#xff0c;下载在这两者之间的所有数据到本机&#xff0c;以便于进行后续的分析工作。 例如&#xff1a;2022.2.23 - 2022.2.26 主要流程 主流程在遍历每个月中实现 当月份小于10时&#xff0c;前面加‘0’&am…

算力军备竞赛白热化 “卖铲人”联想集团竞争力如何?

继微软通过OpenAI推出GPT系列、谷歌推出Bard和PaLM-E2之后&#xff0c;国内AI大模型也呈百家争鸣态势&#xff0c;年初至今&#xff0c;国内科技巨头几乎都发布了自研AI大模型产品&#xff0c;AI竞赛全面升级的背后&#xff0c;是全球科技巨头们对算力的争夺&#xff0c;作为算…

【kali学习笔记】利用第三方服务对目标进行被动信息收集

一、被动信息收集 1、概念&#xff1a; 信息收集的方式分为两种&#xff1a;被动收集和主动收集。被动信息收集方式是指利用第三方的服务对目标进行访问了解&#xff0c;比如Google搜索。主动信息收集方式&#xff1a;通过直接访问、扫描网站&#xff0c;这种流量流经网站的行…

Qt 中动态加载窗口(C++)

在编程中&#xff0c;我经常会遇见要根据用户触发按钮&#xff0c;动态生成窗口的情况。在此有两种方法可以动态生成窗口&#xff1a;一&#xff1a;直接在槽函数中调用窗口类。二&#xff1a;将 **.ui 添加到资源文件&#xff0c;通过 QUiLoader 加载。 现将两种方法介绍如下…

redhat 6.4安装oracle11g RAC (二)

创建一个asm Disk Group Name 组&#xff0c;并给一个名称ORC&#xff0c;并选择下面的三块盘&#xff0c;然后 Next错误的原因是由于磁盘数和冗余层级不匹配&#xff1a; 如果创建用来存放OCR和VOTEDISK的ASM磁盘组&#xff0c;那么External、Normal、High三种冗余级别对应的…

企业文件安全必知!掌握低代码平台的应用,事半功倍保障文件安全

在数字时代的今天&#xff0c;企业档案的安全变得比以往任何时候都更加重要。这些文件可能包含有关客户的敏感信息或必须始终保护的专有业务数据。但是&#xff0c;保护这些文件可能是一个复杂且耗时的过程&#xff0c;对于没有专门IT团队的公司来说尤其如此。这就是低代码平台…

组合式API【Vue3】

Vue3组合式API体验 通过一个 Counter&#xff08;自增&#xff09;案例 体验Vue3新引入的组合式API Vue2的代码&#xff1a; <template><button click"addCount">{{count}}</button> </template> <script> export default {data(){r…

SynchronousQueue的TransferQueue源码分析

QNode的源码信息分析 一行一行的分析大概内容&#xff1b;下面会省略大量的CAS操作当前节点可以获取到的next节点item在生产者和消费者下有所不同。生产者是有数据。消费者为null。waiter为当前线程isData属性是用来区分消费者和生产者的属性。值得一提的是最终生产者需要将it…

2023年我国省市县的高新技术企业数量(Excel/Shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平&#xff01;比如一个城市的金融企业较多&#xff0c;那这个城市的金融产业肯定比较发达&#xff1b;一个城市的制造业企业较多&#xff0c;那这个城市的制造业肯定比较发达。 目前&#xff0c;在城…