线程机制

进程与线程

进程(process):程序的一次执行, 它占有一片独有的内存空间,可以通过windows任务管理器查看进程

线程(thread):是进程内的一个独立执行单元,是程序执行的一个完整流程, 是CPU的最小的调度单元

应用程序必须运行在某个进程的某个线程上,一个进程中至少有一个运行的线程: 主线程,  进程启动后自动创建,一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的

一个进程内的数据可以供其中的多个线程直接共享,多个进程之间的数据是不能直接共享的

线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用

何为多进程与多线程

多进程运行: 一应用程序可以同时启动多个实例运行

多线程: 在一个进程内, 同时有多个线程运行

比较单线程与多线程

多线程:优点:能有效提升CPU的利用率,创建多线程开销;缺点:线程间切换开销,死锁与状态同步问题

单线程:优点:顺序编程简单易懂;缺点:效率低

JS是单线程还是多线程

js是单线程运行的

但使用H5中的 Web Workers可以多线程运行

浏览器运行是单线程还是多线程

都是多线程运行的

浏览器运行是单进程还是多进程

有的是单进程:FireFox,老版本的IE

有的是多进程:chrome,新版本的IE

如何查看浏览器是否是多进程运行的呢

查看任务管理器-->进程

浏览器内核

支撑浏览器运行的最核心的程序

不同的浏览器可能不一样

Chrome, Safari : webkit

firefox : Gecko

IE : Trident

360,搜狗等国内浏览器: Trident + webkit

内核由很多模块组成

js引擎模块 : 负责js程序的编译与运行(主线程)

html,css文档解析模块 : 负责页面文本的解析(主线程)

DOM/CSS模块 : 负责dom/css在内存中的相关处理 (主线程)

布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)(主线程)

定时器模块 : 负责定时器的管理(分线程)

DOM事件响应模块 : 负责事件的管理(分线程)

网络请求模块 : 负责ajax请求(分线程)

JS代码块

JS中的代码块是指由<script>标签分割的代码段。JS是按照代码块来进行编译和执行的,代码块间相互独立(即就算代码块1出错,但不影响代码块2的加载和执行),但变量和方法共享。

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
<script type="text/javascript">
console.log("这是代码块一");
var a = 100;
function show() {
console.log("show")
}
</script> <script type="text/javascript">
show();
console.log ("这是代码块二");
console.log(a)
</script>
</head>
<body> </body>
</html>

结果可以看到第一个代码块中的方法和变量可以在第二个代码块中访问,但是需要注意的是:需要先定义在使用,因为js是同步并且单线程的,从上往下执行

HTML页面中JS的加载原理

在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面。

同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它,在这个过程中,页面的渲染和用户互交完全被阻塞。

由于现代浏览器都允许并行下载JS文件,因此<script>标签在下载外部资源时不会阻塞其他的<script>标签。遗憾的是JS下载过程仍然会阻塞其他资源的下载。

JavaScript的单线程

JS语言的一大特点就是单线程,也就是说,同一个时间只能做一件事情。之所以是单线程,是因为与它的用途有关,作为浏览器脚本语言,JS的主要用途是与用户互动以及操作DOM。

这决定了它只能是单线程,否则会带来复杂的同步问题。

先来看一段代码然后再来详细的说明js的运行机制,下面的一段代码执行顺序是什么

console.log(1);
setTimeout(function () {
console.log(2);
}, 0);
console.log(3);
console.log(4);

测试得到的结果是1,3,4,2,至于为什么会这么执行,原因就是因为js是单线程的(同一时间只能做一件事情),js的任务队列,同步任务先执行,后执行异步任务(setTimeout就是一个异步任务)

js引擎执行代码的基本流程

先执行初始化代码: 包含一些特别的代码,比如:设置定时器,绑定监听,发送ajax请求。 后面在某个时刻才会执行回调代码

setTimeout(function () {
console.log('timeout 3') //
}, 3000) setTimeout(function () {
console.log('timeout 2') //
alert('2222') //
}, 2000) alert('提示...') // 1, 暂停当前主线程的执行,同时暂停计时,点击确定后恢复主线程执行和计时
console.log('alert之后') //
 
JavaScript的任务列队

JS任务可以分为两种:一种是同步任务,另一种是异步任务。注意,只有主线程空了,才会去读取"任务队列",这就是JS的运行机制,这个过程会不断重复。

同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕了,才会执行后一个任务。

异步任务:在主线程之外,还存在一个“任务列队”,异步任务就是不进入主线程,而是进入“任务列队”的任务,只有“任务列队”通知主线程,某个异步任务可以执行了并且同步任务执行完毕,该任务才会进入主线程执行。

 
 
 
Javascript的事件和回调函数

"任务列队"是一个事件的列队,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。所谓"回调函数",就是那些会被主线程挂起的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。"任务队列"是个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

 
 
 
 
定时器事件

"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。定时器功能主要由setTimeout()和setInterval()这两个函数来完成

它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeou()方法:

setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数    

console.log(1)
setTimeout(function (){
console.log(2)
}, 1000);
console.log(3)

上面代码的执行结果是1=>3=>2,因为setTimeout()将第二行推迟到1000毫秒之后执行

如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后立即执行

console.log(1)
setTimeout(function (){
console.log(2)
}, 0);
console.log(3)

上面代码的执行结果是1=>3=>2,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。

总之,setTimeout(fn, 0)的含义是,指定某个任务在主线程最早的空闲时间执行,也就是说尽可能早的执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事情都处理完,才会得到执行。

需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程(js是单线程的也就是说只有一个主线程)才会去执行它指定的回调函数,要是当前代码耗时很长,有可能要等很久

所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行

//只打印A,B不会执行,因为while一直在执行,同步代码还没执行完,异步不会执行
console.log('A');
setTimeout(function () {
console.log('B');
}, 0);
while (1) { }

来看下这段程序,执行结果是打印了四次4,因为先执行主线程的循环,setTimeout属于异步任务,被挂起了,所以是for先执行完了再执行setTimeout

for (var i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i); //打印四次4
}, 1000);
}

这段程序执行的结果是1,2,3,4,是因为变量i的作用域的关系

for (let i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i); //1,2,3,4
}, 1000);
}
 
异步任务
setTimeout和setInterval,DOM事件,ES6中的Promise
 
 
 
事件循环模型
Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

为了更好地理解Event Loop,请看下图

stack:所有的代码都是在此空间中执行的

所有代码分类

初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码

回调执行代码(异步代码): 处理回调逻辑

js引擎执行代码的基本流程

初始化代码===>回调代码

模型的2个重要组成部分:

事件管理模块(WebApis),回调队列(callback queue)

模型的运转流程

执行初始化代码, 将事件回调函数交给对应模块管理

当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中

只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行

function fn1() {
console.log('fn1()')
}
fn1() // document.getElementById('btn').onclick = function () {
console.log('处理点击事件') // 触发事件的时候才会执行
} setTimeout(function () {
console.log('到点了') //
}, 2000) function fn2() {
console.log('fn2()')
}
fn2() //

H5 Web Workers多线程

为了利用多核CPU的计算功能,HTML5提出了web worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不能操作DOM,所以这个新标准并没有改变JS单线程的本质。

JavaScript的运行机制的更多相关文章

  1. 简述JavaScript的运行机制

    想要理解JavaScript的运行机制,需要分别深刻理解以下几个点: · JavaScript的单线程机制 · 任务队列(同步任务和异步任务) · 事件和回调函数 · 定时器 · Event Loop ...

  2. 浅谈javascript的运行机制

    积累一下这几天学的,记录一下: 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程 ...

  3. 用一个小例子来谈谈javascript的运行机制

    先上例子! <script type="text/javascript"> console.log('博'); setTimeout(function(){ conso ...

  4. [转]JavaScript线程运行机制

    从开始接触js时,我们便知道js是单线程的.单线程,异步,同步,互调,阻塞等.在实际写js的时候,我们都会用到ajax,不管是原生的实现,还是借助jQuery等工具库实现,我们都知道,ajax可以实现 ...

  5. 浏览器UI多线程及JavaScript单线程运行机制的理解

    在上一篇博客中,我对jQuery的队列(queue)机制和动画(animate)机制做了一个深入的解析,在animate的实现机制其核心是依靠queue来完成的,其中在jQuery的链式调用部分,之前 ...

  6. JavaScript的运行机制!!!很重要很重要!!!!!!请看大神操作!

    https://juejin.im/post/59e85eebf265da430d571f89

  7. 深入理解JavaScript运行机制

    深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...

  8. JavaScript 运行机制详解:再谈Event Loop

    原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...

  9. JavaScript 运行机制详解:深入理解Event Loop

    Philip Roberts的演讲<Help, I'm stuck in an event-loop>,详细.完整.正确地描述JavaScript引擎的内部运行机制. 一.为什么JavaS ...

随机推荐

  1. apache log4j打印日志源码出口

    Throwable.class: public void printStackTrace(PrintStream s) { synchronized (s) { s.println(this); St ...

  2. Tomcat8源码笔记(二)Bootstrap启动

    TOMCAT源码调试入口是Bootstrap类的main方法,我的启动参数VM: -Dcatalina.home=E:/Tomcat_Source_Code/apache-tomcat-8.0.53- ...

  3. 内置函数二(lambda函数,sorted(),filter(),map(),递归函数,二分法查找)

    一,匿名函数 lambda表⽰示的是匿名函数. 不需要⽤用def来声明, ⼀一句句话就可以声明出⼀一个函数 语法:    函数名 = lambda 参数: 返回值 注意: 1. 函数的参数可以有多个. ...

  4. hadoop 核心概念及入门

    Hadoop Hadoop背景 什么是HADOOP HADOOP是apache旗下的一套开源软件平台HADOOP提供利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分布式处理,HADOOP的核 ...

  5. Redis实现分布式锁的正确使用方式(java版本)

    Redis实现分布式锁的正确使用方式(java版本) 本文使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景. 分布式锁一般有三种实现方式: 1. 数据库乐观锁: ...

  6. Java学习笔记之——switch-case条件结构

    语法: switch(变量){ case 常量1: 执行语句: break: case 常量2: 执行语句: break: case 常量3: 执行语句: break: ………… default: 语 ...

  7. JDK源码解析之Java SPI机制

    1. spi 是什么 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件. 系统设计的各个抽象,往往 ...

  8. objectLiteral.js

    // 1.任意参数的加法运算 function add(){ var sum = 0 for(var i=0;i<arguments.length;i++){ if(!isNaN(argumen ...

  9. JavaScript数组学习总结

    数组   数组 1.数组:数组是一组数据(数据类型不限,任意)的有序集合===>我们写代码,一般一个数组只放一种数据类型的数据 2.我们写代码,一般一个数组只放一种类型的数据 3.注意: 大多数 ...

  10. 【读书笔记】iOS-后台运行模式

    苹果在关于后台模式的文档中称:“这个配置项应该尽可能少的使用,而且最好只给那些提供通知服务的应用使用.如果有在后台运行的替代方法,就应该使用替代方法.比如,如果应用能使用显著位置变化接口来接受位置变动 ...