多线程一之并发编程的基石

并发编程的基石之多线程概念建立

image-20200131110622023

什么是线程、进程

操作系统、进程、线程的包含关系

操作系统是包含多个进程的容器,而每个进程又都是容纳多个线程的容器

单核CPU可以执行多个进程,是因为操作系统的调度

cpu里的一个核,在某一个时间点只能处理一个进程中的一个线程,正是因为这样,所以单核处理器无法并行执行,而多核处理器可以并行

并行并发串行针对进程里的线程之间的运行状态来说的,而同步异步阻塞非阻塞是针对执行线程的被调用者和调用者来说的,两者没有联系

Oracle文档的官方定义

进程:使用fork(2)系统调用创建的UNIX环境(例如文件描述符、用户ID等),它被设置为运行程序

线程:在进程上下文中执行的一系列指令

什么是进程

进程的英文是Process,指的是程序的一次执行。在用户下达运行程序的命令后,就会产生进程

任务管理器

image-20200124094141560

总结:

进程是程序(这里可以理解为我们写的代码)的真正运行实例了,是资源分配的基本单位

什么是线程

定义:线程是CPU的基本调度单位,每个线程执行的都是进程代码的某个片段

实例演示:用活动监视器的CPU栏目看java进程的线程数的变化

创建100个线程,用活动监视器的CPU栏目看java进程的线程数的变化,可以看到一个java进程的线程数有120+,10秒后消失

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 描述:创建100个线程,用活动监视器的CPU栏目看Java线程数量的变化,10秒后线程消失
*
*/
public class Create100Threads {
public static void main(String[] args) throws InterruptedException {

for (int i=0;i<100;i++){

System.out.println(Thread.currentThread().getName());
new Thread(new Runnable() {
@Override
public void run() {

System.out.println("------");
System.out.println(Thread.currentThread().getName());
try {

Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------");


}
}).start();

}
}
}

image-20200131115001187

image-20200131115043885

用房子作比喻

房子是一个容器(比喻进程),拥有某些属性(例如建筑面积,卧室数量等),但是房子本身并没有主动做任何事。

做事情的是住在里面的人(比喻线程)。

人在房子里工作、睡觉、看电视,这些比喻线程在执行各种功能的代码。

进程是线程的容器,包含存储器等资源,而线程利用这些资源来执行代码,最终产生结果。

进程和线程的关系和区别

image-20200124232016044

image-20200124232206872

image-20200124232225617

1起源不同

先有进程 ,因为CPU的执行速度比外设快,为了提高CPU的利用率,后有线程

2概念不同

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位

线程是CPU调度单位

3内存共享方式不同

默认情况下,内存无法与其他进程共享

线程共享由操作系统分配给其父进程的相同内存块

4拥有资源不同

进程拥有独立内存,而线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但是它可与同属性一个进程的其他的线程共享进程所拥有的全部资源

线程共享的内容
  1. 进程代码段
  2. 进程的公有数据(利用这些共享数据,线程很容易实现相互之间的通讯
  3. 进程打开的文件描述符
  4. 信号的处理器
  5. 进程的当前目录
  6. 进程用户ID与进程组ID
线程独有的内容
  1. 线程ID
  2. 寄存器组的值
  3. 线程的堆栈
  4. 错误返回码
  5. 线程的信号屏蔽码

数量不同

一个程序至少有一个进程,一个进程至少有一个线程

开销不同

线程的创建终止时间比进程短

同一进程内的线程切换时间比进程切换短

同一进程的各个线程间共享内存和文件资源,可以不通过内核进行通信

相同点

就绪、等待、运行等生命周期

Java和多线程的关系

Java天生支持多线程、语言排名高

Java线程会一对一映射到操作系统的内核线程,有些语言创建的是虚拟线程,不是实际存在的,而java创建的线程是实实在在存在的,并在操作系统中看到

image-20200131130257472

注:

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java虚拟机环境。

JVM会自动启动一些线程

1
2
3
4
5
6
7
8
/**
* 描述:即使代码中不显示创建线程,在运行main时,JVM也会自动启动其他的线程
*/
public class JavaAndThreads {
public static void main(String[] args) {
System.out.println("Hello Threads!");
}
}

image-20200125222804247

Signal Dispatcher

把操作系统发来的信号分发给适当的处理程序,是用来连接操作系统和应用程序的

Finalizer

负责对象的finalize()方法,每个对象在结束的时候可能实现finalize()这个方法,但是随着java语言的发展,finalize()方法已经不推荐使用

Reference Handler

和GC息息相关,会把每一个对象的引用记录在案,并配合GC进行垃圾回收

main

主线程,用户程序入口

以上线程都不是自己创建的,而是JVM帮我们创建的,从以上可以看出,Java语言和多线程息息相关

## 多线程

什么是多线程

多线程的概念

如果一个程序允许两个或以上的线程,那么它就是多线程程序。多线程是指在单个进程中运行多个线程

比喻

客厅比喻

客厅:公共空间(进程)

厕所:锁

独立房间:线程独享空间

花园浇水:线程合作

火锅的比喻

大火锅一个人吃,就是单进程单线程

大火锅多人吃,就是单进程多线程

分开吃小火锅,就是多进程多线程

我吃火锅,别人吃火锅底料,那时我就拥有锁,别人拿不到锁

举例

抢火车票

image-20200125231734355

#### 独立任务和共享任务

image-20200125232505350

image-20200125232530479

为什么需要多线程

最主要的目的就是提高CPU的利用率

1.提高处理速度

2.避免无效等待(IO的时候可以做别的事)

3.提高用户体验,避免卡顿,缩短等待时间

  • 并行处理,提高性能,通常是服务器领域(例如Tomcat),用多个线程去接收进来的HTTP请求,而不是排队等待单一的线程处理
  • 在Android开发中,主线程的重要任务之一是绘制屏幕界面,该线程中不允许进行IO操作或网络请求,目的就是避免卡顿,影响用户的交互

4.便于编程建模

  • 把大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,就简单多了

计算机性能定律

摩尔定律失败,阿姆达尔定律登上舞台

摩尔定律

当价格不变时,集成电路可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍

阿姆达尔定律

处理器越多,执行程序就越快,但有上限,取决于程序中串行部分和并行部分的比例,而并行的比例越高,多处理器的效果就越明显

什么场景会用到多线程

什么时候需要新开线程

通常在需要进行耗时任务的时候,例如执行磁盘IO读写,或者从网络获取信息的时候

为了同时多件不同的事

  • 开网页同时听音乐
  • 后台定制任务

为了提高工作效率、处理能力

  • tomcat
  • 并行下载
  • NIO、AIO

需要同时有很大的并发量的时候

  • 压测

多线程的局限性

性能问题

  • 上下文切换带来的消耗

异构化任务

  • 任务结构不一样导致很难高效并行

带来线程安全问题

  • 包括数据安全问题(例如i++总数不一致)以及线程带来的活跃性问题(线程饥饿、死锁)

串行、并行、并发

image-20200126003310661

image-20200126003340098

串行、并行

串行是大家排队一个一个来,并行是大家一起来

并行、并发

概念非常相似,从而难以区分

本身对于并发和并行的概念就没有明确:并发可以对应到两个不同的概念

并发

image-20200126005930100

只有一个处理器(单核处理器),逻辑上的同时运行

并行

image-20200126011741341

多处理器(或多核处理器),物理上的同时运行

image-20200126013844980

并行的概念

真正的“同时”运行,在同一时刻,有多个任务同时执行

例如,在多核处理器上,有两个线程同时执行同一段代码

单核处理器是无法实现并行的,因为单核处理器无法在同一时刻执行多个任务

并发的2种概念

1.形容多个任务的执行状态

两个或多个任务可以在重叠的时间段内启动、运行和完成,此时并行(两个线程同时执行)一定是并发,但不意味着并发一定要求是并行

image-20200131153501670

image-20200131153958529

在物理层面上并发其实还是串行,不过是在多个程序上快速的来回切换而不是一个一个执行所以在用户看来就相当于多

个程序同时执行所以叫做并发

2.对“并发性”的简称

不同的部分可以无序或者同时执行,且不影响最终的执行结果

无论是单核还是多核,只要能相互切换并能得到最终结果,就都具有并发性

并发性是并发执行和并行执行的前提条件和必要条件

在不同核心数的计算机上的不同表现

此时,并发和并行的概念不在同一个维度上

用单核多核CPU来辨别概念

程序具备并发性,但不并行执行–单核并行程序

程序具备并发性,且并行执行–多核

既不并行执行,也不具备并发执行–单核串行

并行执行,但程序不具备并发性–不可能

是什么让并发和并行成为可能

CPU升级

操作系统升级

即使是单核CPU也可以使用时间片切换的方式(抢占式)不停的切换,抢占式是CPU最主要的处理方式

编程语言升级

高并发

什么是高并发

高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请求(例如:12306的抢票情况;天猫双十一活动)。

该情况的发生会导致系统在这段时间内执行大量操作,例如对资源的请求,数据库的操作等。

高并发和多线程的联系和不同

多线程是解决方案,高并发是状态

为了解决高并发的问题,可以使用多线程来提高硬件的利用率,来获取更多的资源

高并发不一定通过多线程来解决

有时候数据库有很大的压力,可以使用redis来承受压力

高并发并不意味着多线程,比如redis的使用,也是解决高并发的一种方式,使用redis承受数据的压力,就减轻了数据库的压力

高并发有哪些指标

  • QPS(Queries Per Second):每秒查询数(每秒请求数)

    每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。

  • 带宽:峰值流量和页面平均大小

  • PV(Page View):页面浏览量,一天之内的页面点击量

  • UV(Unique Visitor):独立访问,在UV上的用户访问数量,UV一定小于PV

  • 并发连接数(The number of concurrent connections):某个时刻服务器所接受的请求的数目,通常大于服务器用户在线数量,而对于服务器来说并发连接数是有一定上限的,所有可以作为参考

  • 服务器平均请求等待时间(Time per request:across all concurrent requsets):服务器处理一个请求所需要的时间,把众多的数据汇总起来求出平均时间

注:

IP和UV不同

同步与异步、阻塞与非阻塞

同步与异步

被调用者是否主动告诉调用者结果

同步:被调用者不会主动告诉调用者结果

image-20200131211235549

image-20200131211858795

同步就是被调用者不会主动告诉调用者调用的结果,如以上图,假设去请求服务器中的一张图片,开始请求的时候服务器一直在处理,用户就等待,服务器处理完成,用户获得结果

同步异步这里指的是被调用者(也就是服务器)的行为,而不是请求方的行为。在没有得到结果之前,服务端就不返回任何结果

异步:调用在发出之后,服务端会立即告诉调用方“我收到你的请求了,会处理的”,服务器就会去处理请求,而用户可以继续做自己的事情,服务器处理完请求会自动将结果交给用户

image-20200131211325138

image-20200131211954710

image-20200131212413006

image-20200131220604116

阻塞与非阻塞

对于阻塞和非阻塞要站在两个角度去考虑

  • 站在线程状态的角度,阻塞时当前线程不能执行,需要等待时间到期或者有人唤醒

  • 站在线程发出请求的角度,通常是HTTP请求的角度,站在调用者角度

作为调用者,调用一个东西之后,在结果返回之前,是否能做别的事情

总结

同步、异步指的是被调用者,调用结束后是否主动返回结果

阻塞、非阻塞指的是调用者,调用后是否可以去做别的事

阻塞是线程基本状态中的三种指的是:blocked、waiting、timed_waiting

image-20200131221906268

面试常考

线程和进程相同和不同

并行和并发的区别

多线程就是高并发吗

同步阻塞和异步非阻塞的关系

多线程可以提高执行效率,但有哪些弊端

在单核CPU上运行多线程程序有意义吗

有意义,虽然在单核CPU执行真正的并行是不可能的,因为只有一个处理器,但对于应用程序而言,用户并不知道程序运行在单核CPU还是多核CPU,所以在编写的时候一定一多核CPU为准。首先会编写一个多核CPU程序,其次,假设是多线程,当一个线程执行缓慢并且阻塞的时候,其他线程就可以利用这些时间来做其他的事情,来提高CPU的利用效率

补充

假设我们有3件事情(事情A、事情B和事情C)要完成,完成每件事情所需的时间包括实际投人时间(如做些准备活动所需的时间)和等待的时间,完成这些事情所需的时间为:事情A耗时15分钟(实际投入5分钟,等待10分钟)、事情B耗时10分钟(实际投人2分钟,等待8分钟)、事情C耗时10分钟(实际投入10分钟,无等待耗时)。那么,我们有3种方式来完成这几件事情,如图所示:

串行( Sequential):如图(a)所示。先开始做事情A,待其完成之后再开始做事情B,依次类推,直到事情C完成。这实际上顺序逐一完成几件事情,只需要投入一个人。在这种方式下3件事情总共耗时35(15+10+10)分钟。

并发( Concurrent):如图(b)所示。这种方式也可以只投入一个人。这个人先开始做事情A,事情A旳准备活动做好后(此时消耗了5分钟),在等待事情A完成的这段时间内他开始做事情B。为事情B的准备活动花了2分钟之后,在等待事情B完成的这段时间内他开始做事情C,直到10分钟之后事情C完成。这整个过程实际上是以交替的方式利用等待某件事情完成的时间来做其他事情。在这种方式下3件事情总共耗时17(5+2+10)分钟,这比第1种方式节约了一半多的时间。

并行( Parallel):如图(c)所示。这种方式需要投入3个人,每个人负责完成其中一件事情。这3个人在同一时刻开始齐头并进地完成这些事情。在这种方式下3件事情总共耗时15分钟(取决于耗时最长的那件事情所需的时间),比并发的方式节约了2分钟的时间。

文章目录
  1. 1. 并发编程的基石之多线程概念建立
    1. 1.1. 什么是线程、进程
      1. 1.1.1. 操作系统、进程、线程的包含关系
      2. 1.1.2. Oracle文档的官方定义
      3. 1.1.3. 什么是进程
      4. 1.1.4. 什么是线程
      5. 1.1.5. 进程和线程的关系和区别
        1. 1.1.5.1. 1起源不同
        2. 1.1.5.2. 2概念不同
        3. 1.1.5.3. 3内存共享方式不同
        4. 1.1.5.4. 4拥有资源不同
          1. 1.1.5.4.1. 线程共享的内容
          2. 1.1.5.4.2. 线程独有的内容
        5. 1.1.5.5. 数量不同
        6. 1.1.5.6. 开销不同
        7. 1.1.5.7. 相同点
      6. 1.1.6. Java和多线程的关系
      7. 1.1.7. 什么是多线程
        1. 1.1.7.1. 比喻
          1. 1.1.7.1.1. 客厅比喻
          2. 1.1.7.1.2. 火锅的比喻
        2. 1.1.7.2. 举例
          1. 1.1.7.2.1. 抢火车票
      8. 1.1.8. 为什么需要多线程
        1. 1.1.8.1. 计算机性能定律
      9. 1.1.9. 什么场景会用到多线程
      10. 1.1.10. 多线程的局限性
    2. 1.2. 串行、并行、并发
      1. 1.2.1. 串行、并行
      2. 1.2.2. 并行、并发
        1. 1.2.2.1. 并发
        2. 1.2.2.2. 并行
        3. 1.2.2.3. 并行的概念
        4. 1.2.2.4. 并发的2种概念
        5. 1.2.2.5. 用单核多核CPU来辨别概念
        6. 1.2.2.6. 是什么让并发和并行成为可能
    3. 1.3. 高并发
      1. 1.3.1. 什么是高并发
      2. 1.3.2. 高并发和多线程的联系和不同
      3. 1.3.3. 高并发有哪些指标
    4. 1.4. 同步与异步、阻塞与非阻塞
      1. 1.4.1. 同步与异步
      2. 1.4.2. 阻塞与非阻塞
      3. 1.4.3. 总结
    5. 1.5. 面试常考
    6. 1.6. 补充
|