一图胜千言
进程
进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。
线程
线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位)
线程是不能单独存在的,它是由进程来启动和管理的。
两者关系
-
计算机资源独立分配到各个进程,进程之间 互相独立(可以通信,但是代价较大)
-
一个进程由一个或多个线程组成,是包含关系
-
同一进程下的各个线程之间共享程序的内存空间
-
不同进程之间也可以通信,不过代价较大
在浏览器中打开一个网页相当于新起了一个进程(进程内有自己的多线程)
线程池
线程池是一种用于管理线程的集合,以提高内存和CPU效率。它通过一次性创建一定数量的线程,并当有新的请求到来时,直接使用已创建的线程来处理请求,从而避免了频繁地创建和销毁线程的开销。线程池中的线程可以被多个请求共享,因此可以更有效地利用系统资源。
使用浏览器线程池
浏览器并没有提供直接的线程池接口。浏览器主要使用事件循环模型来处理异步任务,而不是直接使用线程池。事件循环模型将任务分为宏任务和微任务,宏任务包括脚本整体(整体代码script)、setTimeout、setInterval、setImmediate、I/O、UI渲染,微任务包括Promise.then/catch/finally、MutationObserver、process.nextTick(Node.js环境)。
在JavaScript中,虽然无法直接使用线程池,但可以使用Web Workers API来创建在后台线程中运行的任务,其不能操作DOM。Web Workers允许你在浏览器中创建多个线程,以便执行复杂的任务而不影响页面的性能。注意:这依然不能改变js是单线程的事实。
// 创建一个新的Worker对象,并传递要运行的脚本文件的URL:
var worker = new Worker('script.js');
// 通过postMessage()方法向后台线程发送数据:
worker.postMessage('Hello, worker!');
// 在后台线程中处理发送过来的数据:
self.onmessage = function(event) {
console.log('Worker received: ', event.data);
// 执行后台线程中的任务
// ...
// 发送结果回主线程
postMessage('Done!');
};
// 在主线程中接收后台线程的响应:
worker.onmessage = function(event) {
console.log('Main thread received result: ', event.data);
};
MutationObserver
MutationObserver 是一个用于监视DOM树变化的接口。当DOM树中的任何变动,比如节点的增减、属性的变动、文本内容的变动等发生时,MutationObserver 都会触发相应的回调函数。与事件触发不同,MutationObserver 是异步触发的,DOM的变动并不会立刻触发,而是要等到当前所有DOM操作都结束才触发。
使用 MutationObserver 时,需要创建一个新的实例,然后传入一个回调函数。当DOM发生变化时,这个回调函数就会被调用。回调函数接受两个参数:一个是 MutationObserverEvent 对象,另一个是 MutationObserver。
MutationObserver 有以下特点:
- 它等待所有脚本任务完成后,才会运行(即异步触发方式)(微任务)。
- 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。
总之,MutationObserver 是一个允许我们异步观察DOM变动的接口,当DOM发生任何变动时,它都可以触发我们提供的回调函数。
// 创建一个新的MutationObserver实例
var observer = new MutationObserver(function(mutations) {
// 回调函数
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
// 配置观察选项
var config = { attributes: true, childList: true, subtree: true };
// 指定要观察的DOM元素
var targetNode = document.getElementById('someElementId');
// 开始观察DOM元素
observer.observe(targetNode, config);
// 停止观察DOM元素
// observer.disconnect();
网络线程
浏览器会为每个页面创建一个独立的渲染线程,但网络线程是公用的。这意味着多个页面可以共享同一个网络线程来处理网络请求。
这种设计可以充分利用多核CPU的性能,同时减少线程的创建和销毁开销。通过共享网络线程,浏览器可以更高效地处理多个页面的网络请求,提高网页的加载速度和性能。
浏览器网络线程针对不同源的网站一般不会共用。每个源(即域名)通常会有自己的网络线程。当浏览器访问不同源的网站时,会为每个源创建一个独立的网络线程,以确保请求的独立性和安全性。
需要注意的是,具体的实现方式可能会因浏览器类型和版本而有所不同。因此,具体的网络线程共享策略可能会有所差异。
浏览器连接池
浏览器连接池是一种技术,它允许在浏览器中创建和管理HTTP连接的集合,以提高网络性能和加载速度。
浏览器连接池的主要目的是重用HTTP连接,以减少创建和关闭连接的开销。当浏览器需要发送HTTP请求时,它可以从连接池中获取一个可用的连接,而不是每次请求都重新建立一个新的连接。这种重用连接的方式可以降低网络延迟和提高页面加载速度。
连接池中的连接可以被多个请求共享,因此可以更有效地利用系统资源。此外,连接池还可以根据需要进行扩展和收缩,以适应不同的网络环境和请求负载。
浏览器连接池通常与HTTP/1.1的持久连接(也称为长连接)一起使用。持久连接允许在同一个TCP连接上发送多个HTTP请求和响应,而不需要为每个请求重新建立连接。这可以减少握手和挥手的时间开销,并提高网络效率。
需要注意的是,浏览器连接池可能并不适用于所有情况。例如,当与不同的服务器进行通信时,可能需要为每个服务器建立单独的连接池。此外,在某些情况下,使用过多的连接池可能会导致系统资源的浪费和性能下降。因此,在使用浏览器连接池时需要根据具体情况进行权衡和优化。
下面是一个简单的示例代码,演示如何使用浏览器连接池:
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class BrowserConnectionPoolExample {
public static void main(String[] args) {
// 创建连接池
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.executor(executor) // executor实例,用于执行请求
.connectTimeout(Duration.ofSeconds(10)) // 连接超时时间
.build(); // 创建HttpClient实例
// 提交请求给连接池
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://example.com")) // 设置请求的URI
.build(); // 创建HttpRequest实例
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); // 使用连接池发送请求并获取响应
// 处理响应
System.out.println(response.body());
}
}
可以看看这篇文章 一文看懂Chrome浏览器运行机制 - 知乎