并发简述
Java在设计之初,就是支持多线程的语言。
在Java学习中,我们经常能听到:高并发、多线程这样的词汇。那么,高并发是什么?线程是什么?高并发与线程有什么关系呢?
那么为什么有进程,又为什么有线程呢?
随着结束的发展,我们的CPU、内存、I/O设备不断迭代,不断朝着更快的方向努力。但是这三者有个核心的矛盾:速度的差异。CPU和内存的速度差异可以描述为:CPU天上一天,而内存地上一年(假设CPU执行一条普通指令需要一天,那么CPU读写内存需要等待一年)。内存和I/O设备的速度差异更大,内存天上一天,I/O设备地上十年。
我们程序大部分都语句需要访问内存,有些还需要访问I/O。根据木桶理论,我们程序的整体性能取决于最慢的操作,也就是读写I/O设备。
为了合理利用CPU的高性能,提高这三者的速度,计算机体系结构、操作系统、编译程序都做出了努力:
- CPU增加了缓存,均衡与内存的速度差异
- 操作系统增加了进程、线程,以分时复用CPU,进而均衡CPU与I/O设备的速度差异
- 编译程序优化指令执行次序,使缓存能够得到更加合理的利用
线程与进程
在oracle的官方文档中,对进程与线程是这样描述的:
翻译过来,就是进程,是通过fork命令创建出来的运行环境,包含了如文件描述符、用户ID等信息,它被设置为运行程序。进程是系统进行资源分配和调度的基本单位,进程作为程序独立运行的载体,可以保障程序正常执行。
线程,是进程上下文中执行的一系列指令。
进程
那么理解下,进程是程序的一次执行。当我们打开某个程序,就会产生一个进程。
打开任务管理器,就可以看到:
那么再理解下,我们下载了一个程序,但是没有打开。这个时候程序只是占用了一定的空间,大部分是磁盘。当我们打开这个程序,它才会耗费一定的CPU、内存等。如果我们的进程占用资源过大,系统为了响应我们的进程,就会造成卡顿。(例如电脑卡了,打开资源管理器,哦吼,某个进程占用CPU99.99999%)
看到过一个很好的比喻:进程是对我们代码的一个实例化。
线程
进程可以看做线程的容器。进程是程序的运行环境,而真正去执行程序的,是一个个线程。
需要注意的是,Java的线程与操作系统的线程是一一对应的。
为什么有线程
在计算机的发展历史中,是先存在的进程。随着计算机的发展,计算机的处理速度远远大于外设的处理速度,为了避免等待造成的浪费,提高程序的执行效率,才出现的线程。
JVM的自有线程
我们随便写个测试函数,然后debug看一下:
@Test
public void test(){
new Thread(()-> System.out.println("=================new thread=================")).start();
System.out.println("==================sleeping==================");
}
debug的线程情况如下所示,可以看到除了主线程外,还有几个jvm的自有线程:
Signal Dispatcher:把操作系统发来的信号分发给适当的处理程序
Finalizer:在学习垃圾回收的时候学到过,如果对象实现了finalize方法,就会加入到一个队列中,然后由JVM创建的低优先级的线程去执行,就是这个线程
ReferenceHandler:是JVM创建的优先级最高的守护线程,作用是在垃圾回收的时,ReferenceHandler主要作用是对需要回收的对象进行后序的回收清理工作。
部分源码:
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
线程与进程的关系
进程 | 线程 | |
---|---|---|
起源不同 | 先有进程 | 因为计算机的处理速度远远大于外设的处理速度,为了提高CPU利用率,才出现的线程 |
概念不同 | 具有独立功能的程序运行起来的环境,是个实例,是系统分配资源与调度的单位 | CPU的基本调度单位 |
内存共享方式不同 | 不同的进程之间的内存通常是不共享的 | 线程之间,是有共享内存的 |
用有资源不同 | 共享 1.进程代码段(重点) 2.进程的公有数据 3.进程打开的文件描述符 4.信号处理器 5.进程当前的目录 6.进程用户ID与进程组ID 私有: 1.线程ID 2.寄存器组的值 3.线程的堆栈(重点) 4.错误返回码 5.线程的信号屏蔽码 | |
数量不同 | 一个进程可以有多个线程,至少一个 | |
开销不同 | 1.线程的创建、终止时间比进程短 2.同一个进程内的线程切换时间比进程切换时间短 3.同一个进程的各个线程之间共享内存与文件资源,可以不同过内核进行通信 | 线程更轻量级 |
并发、并行、串行
下面这张图就可以表示出这三者的区别(非原创,从慕课网视频截取):
这里用生活中的现象举个例子,假设我们有3个小伙伴住在同一间房。
串行
这个屋子里只有一个浴室,那么三个小伙伴只能排队,一个洗完了另外一个才能去。
并发
屋子里只有一个浴室跟一个厨房还有一台电脑。三个小伙伴商量了一下,小伙伴A去洗澡,B去做饭吃饭,C去打游戏;然后A去吃饭,B去打游戏,C去洗澡;最后A去打游戏,B去洗澡,C去吃饭。
并行
这个屋子里有三个卧室,三个小伙伴一人一间,夜晚来了他们都在睡觉。
同步异步、阻塞非阻塞
其实同步异步、阻塞非阻塞是两个维度的事情:
同步异步指的是被调用者是否主动告诉调用者结果
阻塞非阻塞指的是我作为调用者,在结果返回前能不能做别的事情
举个栗子来理解下。
到吃饭的时间了,你肚子有点饿了,而这个时候你妈妈在厨房做饭。
同步
如果你妈妈会主动告诉你饭好了,快来吃吧!这就是同步。
异步
如果你妈妈在生你的气不想搭理你,饭好了就端上桌自己吃,也不喊你,等你闻到香味,或者饿得受不了了去饭桌上一看,诶饭好了,可以吃了!这就是异步。
阻塞
如果你就蹲在厨房门口,饭不好你就不走了,啥事儿也不干了,这就是阻塞。
非阻塞
如果你决定看会电影等饭,这就是非阻塞。
高并发
高并发:同时有很多请求发给服务器
还是上面的例子。
今天游戏出新英雄玛西了,三个小伙伴都非常急想去玩一下,都在急着抢电脑。
高并发与多线程的关系
多线程只是解决高并发的一种手段,但不是唯一的方法。比如上面的栗子中我们可以加一台电脑(扩容?),如果是数据库的高并发,我们可以加缓存等。
高并发的指标
还是上面几个小伙伴抢着玩电脑的栗子:
QPS
每秒查询/请求数
同一时刻有多少个小伙伴过来抢电脑
带宽
PV(PageView)
综合浏览量
这一台电脑一天内的试用次数
UV(UniqueVisitor)
PV基础上的去重
这一台电脑一天内被几个小伙伴使用,一天内一人只算一次
并发连接数
同一时间内有几个小伙伴可以玩电脑
服务器平均请求等待时间
这几个小伙伴平均等待了多久就可以玩到电脑了~