浏览器是单线程的?

大家有时候会听到这么一句话:浏览器是单线程的。这是一个关于浏览器架构的常见误解。

浏览器本身是多进程的

现代浏览器(如 ChromeFirefoxEdge)是多进程架构。每个进程都有其独立的内存空间,互不干扰,这大大增强了浏览器的稳定性。

Chrome 为例,它通常会有以下几个主要进程:

主要进程类型

  • 浏览器主进程 (Browser Process):负责管理浏览器界面(地址栏、书签等)、用户输入、文件存储等。

  • 渲染进程 (Renderer Process):这是最关键的部分,它负责将 HTMLCSSJavaScript 转换为我们看到的网页。通常,浏览器会为每个标签页分配一个独立的渲染进程

  • 插件进程 (Plugin Process):负责运行像 Flash 这样的插件。

  • GPU 进程 (GPU Process):负责处理图形渲染,以提高页面的绘制性能。

多进程架构的优势

这种多进程架构的好处是,如果一个标签页崩溃了,它只会影响该标签页的渲染进程,而不会导致整个浏览器都崩溃。

浏览器的一个页面是多线程的

虽然每个渲染进程是独立的,但它内部并不是单线程的。一个渲染进程内部包含多个线程,例如:

渲染进程内的主要线程

  • 主线程 (Main Thread):这是我们通常所说的”JavaScript 引擎所在的线程”。它负责执行 JavaScript 代码、处理用户事件(如点击、滚动)、执行 DOM 操作和大部分的页面渲染工作。这个线程是单线程的,这意味着它一次只能做一件事。

  • 排版引擎线程 (Layout Thread):负责计算页面元素的布局和位置。

  • 渲染引擎线程 (Painting Thread):负责将元素绘制到屏幕上。

  • 合成线程 (Compositor Thread):负责将不同的图层合成在一起,并将其发送给 GPU 进程。

  • Web Worker 线程:这些是独立的后台线程,用于执行耗时的计算任务,但它们无法直接访问 DOM

架构层次图解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────┐
│ 浏览器 (多进程) │
├─────────────────────────────────────────────────────────┤
│ 浏览器主进程 │ 渲染进程 1 │ 渲染进程 2 │ GPU 进程 │
│ (Browser) │ (Tab 1) │ (Tab 2) │ │
│ │ │ │ │
│ • 界面管理 │ ┌──────────┐ │ ┌──────────┐ │ • 图形 │
│ • 用户输入 │ │ 主线程 │ │ │ 主线程 │ │ 渲染 │
│ • 文件存储 │ │(单线程) │ │ │(单线程) │ │ │
│ │ │ │ │ │ │ │ │
│ │ │• JS 引擎 │ │ │• JS 引擎 │ │ │
│ │ │• DOM 操作 │ │ │• DOM 操作 │ │ │
│ │ │• 事件处理 │ │ │• 事件处理 │ │ │
│ │ └──────────┘ │ └──────────┘ │ │
│ │ │ │ │
│ │ • 排版线程 │ • 排版线程 │ │
│ │ • 渲染线程 │ • 渲染线程 │ │
│ │ • 合成线程 │ • 合成线程 │ │
│ │ • Worker 线程 │ • Worker 线程 │ │
└─────────────────────────────────────────────────────────┘

总结

所以,正确的说法是:

🔍 准确的描述

  • 浏览器是多进程的
  • 浏览器的一个渲染进程是多线程的
  • 一个渲染进程中的”JavaScript 引擎”是单线程的

💡 理解要点

当人们说”JavaScript 是单线程的”时,他们通常指的是在浏览器的主线程中,JavaScript 代码是顺序执行的,一次只能处理一个任务,这也解释了为什么需要事件循环等机制来处理异步操作。

🎯 关键概念

  • 多进程:提供稳定性,进程间相互隔离
  • 多线程:提供并发能力,不同线程处理不同任务
  • 单线程JavaScript 执行的特性,确保代码执行的确定性