热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WKWebView缓存策略

问题描述今天遇到一个问题,网页加载的页面,服务器数据提交更新了,但是iOS这边显示的还是老的内容。页面使用的WKWebView
问题描述

今天遇到一个问题,网页加载的页面,服务器数据提交更新了,但是 iOS 这边显示的还是老的内容。页面使用的 WKWebView 来加载数据的。第一时间我想到的是 WebView 的缓存,那既然安卓端能刷新,应该是 iOS 这边出现了问题,是什么原因导致的呢?首先去苹果官方文档查看 WKWebView 缓存策略描述。

WKWebView 的默认缓存策略

先来看下苹果给的枚举文档

WKWebView 支持的缓存策略枚举

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0, // 默认策略,具体的缓存逻辑和协议的声明有关,如果协议没有声明,不需要每次重新验证cache
    NSURLRequestReloadIgnoringLocalCacheData = 1, // 忽略本地缓存,直接从服务器请求数据
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // iOS 13 才实现,忽略本地缓存数据、代理和其他中介的缓存,直接从从服务器请求数据
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
    NSURLRequestReturnCacheDataElseLoad = 2, // 优先从本地拿数据,且忽略请求生命时长和过期时间。但是如果没有本地cache,则从服务器请求数据
    NSURLRequestReturnCacheDataDOntLoad= 3, // 只从本地拿数据
    NSURLRequestReloadRevalidatingCacheData = 5, //  iOS 13才实现,从原始地址确认缓存数据的合法性后,缓存数据就可以使用,否则从服务器请求数据
};

这里需要注意一下 NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4NSURLRequestReloadRevalidatingCacheData = 5iOS 13 之前并未实现该协议,所以如果使用该协议一定要注意系统版本判断。

NSURLRequestUseProtocolCachePolicy

下面是官方文档提供的默认缓存策略的流程图

WKWebView 缓存策略
苹果官方文档

官方文档描述

For the HTTP and HTTPS protocols, NSURLRequestUseProtocolCachePolicy >performs the following behavior:

  1. If a cached response does not exist for the request, the URL loading system fetches the data from the originating source.

  2. Otherwise, if the cached response does not indicate that it must be revalidated every time, and if the cached response is not stale (past its expiration date), the URL loading system returns the cached response.

  3. If the cached response is stale or requires revalidation, the URL loading system makes a HEAD request to the originating source to see if the resource has changed. If so, the URL loading system fetches the data from the originating source. Otherwise, it returns the cached response.

This behavior is illustrated in Figure 1.
Figure 1 NSURLRequestUseProtocolCachePolicy decision tree for HTTP and >HTTPS

中文翻译

  1. 缓存不存在,则直接从服务器请求数据;
  2. 缓存存在,在 response 头信息中没有指定每次必须进行资源更新,且缓存没有过期,则系统不会从服务器请求拉取数据,而是直接走缓存;
  3. 如果缓存过期了或者要求每次必须校验资源更新,此时会发起一个资源校验更新的请求,如果服务器有更新,则使用服务器返回的新数据,如果没有资源更新则还是使用本地缓存数据

对于上面官方文档的解释只是理解了大概的原理,具体的细节和数据指标还是不清楚,例如:

  1. 什么情况下会缓存数据?
  2. 什么情况下每次都需要校验资源更新?
  3. 缓存过期时间多久?
  4. 校验资源更新的过程是怎么样的?

对于这些,需要了解 HTTP 的缓存协议,实际上,苹果的 WKWebView 默认的缓存策略完全遵守 HTTP 缓存协议,没有做额外的事情。就是说如果你想详细知道 WKWebView 默认的缓存策略,那么你得先去弄清楚 HTTP 的缓存协议。

HTTP 缓存

客户端 默认缓存行为实际上是由 服务器 控制的,客户端服务器 通过 HTTP 请求头响应头 中的缓存字段来交流,进而影响 客户端 的行为。

Pragma、Expires

HTTP 1.0 时代,给客户端设定缓存方式可通过这两个字段。Pragma 是一个通用头,它只有 no-cache 这一个值。

通用头:该字段可以用于请求头,也可用于响应头。(同一个属性在请求头和响应头中意义可能不一样)

作为请求头,表示不使用缓存,直接从服务器获取资源,这是 HTTP 1.0的用法,HTTP1.1 的用法是 Cache-Control:no-cache。不过为了兼容 HTTP1.0 ,一般 Pragma:no-cacheCache-Control:no-cache 联用:

Cache-Control:no-cache
Pragme:no-cache

作为响应头,Pragma : no-cache 的行为并没有被定义,不能保证它的意义和 Cache-Control:no-cache 一致。

Expires(响应头)表示缓存过期的时间点(这里的时间是服务器时间):

Expires: Wed, 14 Apr 2021 12:01:33 GMT

PragmaExpires 的局限:响应报文中 Expires 所定义的缓存时间是相对于服务器上的时间而言的,如果客户端上用户修改了自己设备上的系统时间,那缓存时间能就没有意义了。

Cache-Control

HTTP 1.1 新增了 Cache-Control 来配置缓存信息,主要包括:能否缓存、缓存过期时间、是否每次校验等。

Cache-Control 是通用头:

作为请求头,可选值有

字段名称 说明
no-cache 告知(代理)服务器不直接使用缓存,直接向原服务器发起请求
no-store 所有内容都不会保存到缓存或 internet 临时文件中
max-age=delta-seconds 告知服务器客户端希望接收一个存在时间(Age)不大于 delta-seconds 秒的资源
max-stale[=delta-seconds] 告知(代理)服务器客户端愿意接收一个超过缓存时间的资源,若有定义 delta-seconds 则为 delta-seconds 秒,若没有则为任意超出时间
min-fresh=delta-seconds 告知(代理)服务器客户端希望接收一个在小于 delta-seconds 秒内被更新过的资源
no-transform 告知(代理)服务器客户端希望获取实体数据没有被转换(如压缩)过的资源
only-if-cached 告知(代理)服务器客户端希望获取缓存的内容(若有),而不用向原服务器发去请求
cache-extension 自定义扩展值,若服务器不识别该值将被忽略

作为响应头,可选值有

字段名称 说明
public 任何情况下都得缓存该资源
Private[=“field-name”] 返回报文中全部或部分(若指定了 field-name 则为 field-name 的字段数据)仅开放给某些用户(服务器指定的 share-user,如代理服务器)做缓存使用,其他用户不能缓存这些数据
no-cache 不直接使用缓存,要求向服务器发起(新鲜度校验)请求
no-store 所有内容都不会被保存到缓存或 internet 临时文件中
no-transform 告知服务器缓存文件时不得对实体数据做任何改变
only-if-cached 告知(代理)服务器客户端希望获取缓存的内容(若有),而不用向原服务器发去请求
must-revalidate 当前资源一定是向原服务器发去验证请求的,若请求失败会返回 504(而非代理服务器上的缓存)
proxy-revalidate 与 must-revalidate 类似,但仅能应用于共享缓存(如代理)
max-age=delta-seconds 告知客户端该资源在 delta-seconds 秒内是新鲜的,无需向服务器发请求
$-maxage=delta-seconds 同 max-age,但仅应用于共享缓存(如代理)
cache-extension 自定义扩展值,若服务器不识别该值将被忽略掉
max-age=delta-seconds 告知服务器客户端希望接收一个存在时间(age)不大于 delta-seconds 秒的资源
max-stale[=delta-seconds] 告知服务器客户端愿意接收一个超过缓存时间的资源,若有定义 delta-seconds 则为 delta-seconds 秒,若没有则为任意超出时间
min-fresh=delta-seconds 告知服务器客户端希望接收一个在小于 delta-seconds 秒内被更新过的资源
no-transform 告知服务器客户端希望获取实体数据没有被转换(如压缩)过的资源
only-if-cached 告知服务器客户端希望获取缓存的内容(若有),而不用向服务器发去请求
cache-extension 自定义扩展值,若服务器不识别该值将被忽略

Cache-Control 允许自由组合可选值,用逗号分隔:

// 缓存过期时间是半小时,半小时后,每次都必须向服务器进行资源更新校验
Cache-Control: max-age=1800, no-cache
must-revalidate

坑点:
在苹果官方文档和流程图中有个判断,缓存存在,则需要判断是否需要每次都校验,用的是 >revalidated,那我们是不是可以这样写:Cache-Control: max-age=3600, must->revalidate,设置一个过期时间,但是又希望每次去检验更新。结果是客户端仍然是用的缓存,根本没有网络请求发出去。

HTTP 规范是不允许客户端使用过期缓存的,除了一些特殊情况,比如校验请求发送失败的时候。而 must-revalidate 指令是用来排除这些特殊情况的。带有 must-revalidate 的缓存过期后,在任何情况下,都必须成功 revalidate 后才能使用,没有例外,即使校验请求发送失败也不可以使用过期的缓存。也就是说,有个大前提是缓存过期了,如果缓存没过期客户端会直接使用缓存,并不会发起校验,显然不是字面上每次都校验更新的意思。must-revalidate 命名为 never-return-stale 更合理。而真正每次都校验更新,应该用 no-cache 这个字段。

所以上面错误的写法改成 Cache-Control: max-age=1800, no-cache 这样就可以了,缓存有效期半小时,每次请求都校验更新。

no-cache

作为请求头,告知中间服务器不使用缓存,向源服务器发起请求。
作为响应头,no-cache 并不是字面上的不缓存,而是每次使用前都得先校验一下资源更新。

no-store

作为响应头,带有 no-store 的响应不会被缓存到任意的磁盘或者内存里,no-store 它才是真正的 no-cache

max-age

作为请求头,max-age=0 表示不管 response 怎么设置,在重新获取资源之前,先进行资源更新校验。
作为响应头,max-age=x 表示,缓存有效期是 x 秒。

Cache-Control 的局限

很多时候,缓存过期了但是资源并没有修改,会发送多余的请求和数据;或者资源修改了缓存还没过期,客户端仍然在用缓存。Cache-Control 无法及时和客户端同步。

Last-Modified、If-Modified-Since

为了弥补 Cache-Control 无法及时判断资源是否有更新的不足,有了 Last-Modifiedif-Modified-Since 字段。

Last-Modified

响应头,这次命名没有问题了,这个字段的值就是资源在服务器上最后修改时刻。如:

If-Modified-Since: Wed, 14 Apr 2021 14:01:33 GMT
If-Modified-Since

请求头,客户端通过该字段把 Last-Modified 的值回传给服务端;客户端带上这个字段表示这次请求是向服务端做校验资源更新校验。如果资源没有修改,则服务端返回 304 不返回数据,客户端用缓存;资源有修改则返回 200 和数据。如:

If-Modified-Since: Wed, 14 Apr 2021 14:05:33 GMT
Last-Modified 的启发式(heuristic)缓存

有以下响应信息:

HTTP/2 200
Date: Wed, 14 Apr 2021 14:30:00 GMT
Last-Modified: Wed, 14 Apr 2021 14:10:00 GMT

上面这个响应,没有显示地指明需要缓存,没有 Cache-Control,也没有 Expires,只有 Last-Modified 修改时间,这种情况会产生启发式缓存。缓存时长= (date_value - last_modified_value) * 0.10 ,这是由 HTTP 规范推荐的算法,但规范中仅仅是推荐而已,并没有做强制要求。

如何禁用由 Last-Modified 响应头造成的启发式缓存:正确的做法是在响应头中加上 Cache-Control: no-cache

Last-Modified、If-Modified-Since 的缺陷

无法识别内容是否发生实质性的变化,可能只是修改了文件但是内容没有变化;无法识别一秒内进行多次修改的情况。

ETag、If-None-Match

为了弥补 Last-Modified 的无法判断内容实质性变化的缺陷,于是有了 ETagIf-None-Match 字段,这对字段的用法和 Last-ModifiedIf-Modified-Since 相似,服务器在响应头中返回 ETag 字段,客户端在下次请求时在 If-None-Match 中回传 ETag 对应的值。

ETag

响应头,给资源计算得出一个唯一标志符(比如 md5 标志),加在响应头里一起返给客户端,如:

Etag: "7d6e49djhkd84rh3"
If-None-Match

请求头,客户端在下次请求时回传 ETag 值给服务器。

If-None-Match: "7d6e49djhkd84rh3"

优先级

Pragma > Cache-Control > Expires > Last-Modified > ETag

响应头没有任何缓存字段,每次启动都会发起请求,返回 200

第一次启动,响应头添加 Pragma:no-cacheCache-Control:max-age;第二次启动,会发起请求,返回 304,说明 Pragma 生效了,Pragma > Cache-Control

第一次启动,响应头没有过期时间,只有 Last-Modified;第二次启动,使用缓存,没有发起请求,说明启发式缓存(上文中有提到)生效。

第一次启动,响应头没有过期时间,只有 ETag;第二次启动,会发起请求,返回 304,说明做了资源更新校验。

第一次启动,响应头没有过期时间,同时有 ETagLast-Modified;第二次启动,使用缓存,没有发起请求,启发式缓存生效,说明 Last-Modified > ETag

WKWebView 默认缓存策略

针对上文中提到的几个问题

1. 什么情况下会缓存数据?

客户端第一次启动的时候,如果 响应头 中不包含任何缓存控制字段(ExpiresCache-Control:max-ageLast-Modified 等),那么不会缓存(仍然可能会有物理缓存,只是不使用),下次直接发起请求。如果响应头包含了缓存控制字段,大多数情况下这次数据会被缓存,下次启动的时候执行缓存逻辑判断。

2. 什么情况下每次都需要校验资源更新?

  1. 响应头中包含 Cache-Control:no-cachePragma:no-cache
  2. 响应头中缓存控制字段只有 ETag 字段,没有过期时间和修改时间。

3. 缓存过期时间是多久?

  1. 响应头中 Cache-Control:max-age=1800,表示缓存半小时。
  2. 响应头中 Expires 的值表示过期时刻(服务器时间)。
  3. 响应头中,如果没有上述两个字段,但有 Last-Modified 字段,则触发启发式缓存,缓存时间 = (date_value - last_modified_value) * 0.1

4. 校验资源更新的过程是怎么样的?

revalidated 的指标有两个:Last-Modified(最后修改时刻)、ETag(资源唯一标识)。服务器返回数据时会在响应头中返回上面两个指标(有可能只有1个,也可以2个都有),客户端再次发起请求时会把这两个指标回传给服务器。If-Modified-SinceLast-Modified 的值),If-None-MatchETag 的值)。服务器进行比对,如果客户端的资源是最新的,则返回 304,客户端使用缓存数据;如果服务器资源更新了,则返回 200 和新数据。

总结

WKWebView 默认缓存策略流程总结如下:

  1. 是否有缓存,没有则直接发起请求。有则进行下一步。
  2. 是否每次都得进行资源更新校验(响应头是否有 Cache-Control:no-cachePragma:no-cache 字段),不需要则进入 3,需要则进入 4
  3. 缓存是否过期(响应头,Cache-Control:max-ageExpiresLast-Modified 启发式缓存),没过期则使用缓存,不发起请求。过期了则进入 4
  4. 客户端发起资源更新校验请求(请求头,If-Modified-Since : Last-Modified值If-None-Match : ETag值),如果资源没有更新,服务器返回 304,客户端使用缓存;如果资源有更新,服务器返回 200 和资源。
服务器数据更新后客户端仍然有缓存

如果我们使用了 WKWebView 默认缓存策略(即 NSURLRequestUseProtocolCachePolicy),这个时候 App 什么也做不了,需要后台去处理。

方案一:

添加响应头

如果使用了 Cache-Contol: max-age=xxx 而且没有配置资源更新校验字段。但是这个 xxx 设置的很大,缓存时间很长,即使服务器资源更新了客户端也不会请求新的资源,而是直接使用“没有过期”的资源。所以可以将这个字段设置的小一些外加配置资源更新校验字段。

我们也可以设置每次都会先去校验资源更新,就是默认不走缓存,这样也解决了服务器更新数据,客户端仍然不更新的问题。

Cache-Control:no-cache

方案二

这个方案没有亲自体验过,参考自WKWebView默认缓存策略与HTTP缓存协议

链接加后缀


https://hm.baidu.com/hm.js?e23800c454aa573c0ccb16b52665ac26
http://tb1.bdstatic.com/tb/_/tbean_safe_ajax_94e7ca2.js
http://img1.gtimg.com/ninja/2/2016/04/ninja145972803357449.jpg

可以在资源文件后面加上版本号,每次更新资源的时候变更版本号;还可以在 URL 后面加上了 md5 参数,甚至还可以将 md5 值作为文件名的一部分。
采用上述方法,你可以把缓存时间设置的特别长,那么在文件没有变动的时候,浏览器直接使用缓存文件;而在文件有变化的时候,由于文件版本号的变更,或 md5 变化导致文件名变化,请求的 url 变了,浏览器会当做新的资源去处理,一定会发起请求,所以不存在更新后仍然有缓存的情况。通过这样的处理,增长了静态资源,特别是图片资源的缓存时间,避免该资源很快过期,客户端频繁向服务端发起资源请求,服务器再返回 304 响应的情况(有 Last-Modified/Etag)。

参考代码

NSURLRequest 的默认缓存策略是 NSURLRequestUseProtocolCachePolicy,完全遵循 HTTP 缓存协议。

我们来看以下代码

NSString *url = @"https://xxx";
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSHTTPURLResponse *httpRespOnse= (NSHTTPURLResponse *)response;
    NSLog(@"httpRespOnse== %@", httpResponse);
}] resume];

运行项目,打印结果仅供参考

WKWebView 缓存策略

在这个链接的响应头 Cache-Control: max-age=30,过期时间为 30 秒,但是无论我运行多少次,code 始终返回 200(网段了还是返回数据,code 也是 200)。而且还发现,第一次请求时界面刷新耗费的时间较长,第二次直接显示界面了。这个也验证了默认缓存是有的,但是为什么 code 一直是 200 呢?

苹果系统内部对 304 Not Modified 响应做了特殊处理

  • code 字段,固定返回 200
  • data 字段,因为服务端返回的 304 报文是不带 data 数据字段的,但是苹果又得把 data 通过 completionHandler 回调给客户端,苹果会去缓存中取 data 数据,返回的 data 字段和第一次响应的 data 是同一个。
  • response 字段,返回的是第二次请求 304 的响应头,而不是第一次请求缓存的响应头。第一次和第二次回调的响应头不一致。

综上,苹果内部帮我们处理了 304 Not Modified 响应。对客户端来说,你只需要知道返回 200 就是没有异常,拿着 data 用就行了。至于,数据来自缓存还是来自服务器,缓存有没有过期,需不需要校验资源更新等,都交给苹果吧。

code 都返回 200,那我怎么知道返回的是缓存数据还是服务器数据呢?

我们可以通过以下测试代码来验证下(注意:这是系统自己实现的,并不需要客户端手动添加)

NSString *url = @"https://xxx";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20];
NSDictionary *cachedHeaders = [[NSUserDefaults standardUserDefaults] objectForKey:url];
//设置request headers (带上上次的请求头下面两参数一种就可以,也可以两个都带上)
if (cachedHeaders) {
    NSString *etag = [cachedHeaders objectForKey:@"Etag"];
    if (etag) {
        [request setValue:etag forHTTPHeaderField:@"If-None-Match"];
    }
    NSString *lastModified = [cachedHeaders objectForKey:@"Last-Modified"];
    if (lastModified) {
        [request setValue:lastModified forHTTPHeaderField:@"If-Modified-Since"];
    }
}

[self.couponWebView loadRequest:request];

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
    NSHTTPURLResponse *httpRespOnse= (NSHTTPURLResponse *)response;
    NSLog(@"httpRespOnse== %@", httpResponse);
    // 根据statusCode设置缓存策略
    if (httpResponse.statusCode == 304 || httpResponse.statusCode == 0) {
        [request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
    }
    else {
        [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
        // 保存当前的 NSHTTPURLResponse
        [[NSUserDefaults standardUserDefaults] setObject:httpResponse.allHeaderFields forKey:url];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    // 重新刷新
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.couponWebView reload];
    });
    
}] resume];

第一次打印如下:

WKWebView 缓存策略

第二次打印如下:

WKWebView 缓存策略
总结

对于使用 WKWebView 加载的页面,使用默认的缓存策略,客户端不需要关心返回的数据是否来自缓存,只需要缓存字段配置好后即可。


推荐阅读
  • 判断编码是否可立即解码的程序及电话号码一致性判断程序
    本文介绍了两个编程题目,一个是判断编码是否可立即解码的程序,另一个是判断电话号码一致性的程序。对于第一个题目,给出一组二进制编码,判断是否存在一个编码是另一个编码的前缀,如果不存在则称为可立即解码的编码。对于第二个题目,给出一些电话号码,判断是否存在一个号码是另一个号码的前缀,如果不存在则说明这些号码是一致的。两个题目的解法类似,都使用了树的数据结构来实现。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • 文章目录题目:二叉搜索树中的两个节点被错误地交换。基本思想1:中序遍历题目:二叉搜索树中的两个节点被错误地交换。请在不改变其结构的情况下 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
  • Python15行代码实现免费发送手机短信,推送消息「建议收藏」
    Python15行代码实现免费发 ... [详细]
  • 在Windows10系统上使用VMware创建CentOS虚拟机的详细步骤教程
    本文详细介绍了在Windows10系统上使用VMware创建CentOS虚拟机的步骤,包括准备条件、安装VMware、下载CentOS ISO文件、创建虚拟机并进行自定义配置、设置虚拟机的ISO与网络、进行安装和配置等。通过本文的指导,读者可以轻松地创建自己的CentOS虚拟机并进行相应的配置和操作。 ... [详细]
author-avatar
mobiledu2502913567
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有