多线程是困扰了本人长久的问题,不单单是因为CPU调度和异步执行的理解,最大的山峰是结合程序的角度,具体地理解好创建的每一条线程的意义,但是,经过书本《JAVA多线程编程核心技术》的学习和自己的不懈思考,终于能够破冰!
本章节大概按照书本《JAVA多线程编程核心技术》第一章 : 多线程技能 ,梳理本人学习过程中的所思所想和本人认为的重点内容。借鉴了许多大佬的良心文章,由衷感谢。
进程与线程概述
程序、进程、线程的区别与联系 - SegmentFault 思否
程序: 一堆文本,并不能执行,需要提供资源,安排调度才能执行。而进程则是被提供了资源和安排了调度的程序的动态活动,所以进程是资源分配和CPU调度的一个单位(主要不是基本单位)
线程比起进程更加偏向一个执行体的概念,是CPU分配和调度的基本单位
进程和线程的区别在于:
- 线程的引入是为了解决进程挂起(阻塞)和一个进程只能做一件事的缺陷
- 进程拥有自己独立的资源,线程则没有,多个线程共享进程的资源和地址空间
- 系统没有将线程看作单独的任务
- 进程与进程之间不会互相影响,其中一个进程崩溃后在保护模式下...而线程是一个进程中的不同路径,线程的崩溃会导致进程的崩溃
- 线程的改变只代表了 CPU 执行过程的改变,而没有发生进程所拥有的资源变化。
重难点:
- 线程解决了什么问题,进程的缺陷是什么?
- 内存(资源分配)方面:线程是CPU调度的基本单位但却并不是一个单独的任务,进程是资源分配的基本单位
- 如何理解“线程不具有单独的地址空间”?-- 线程共享内存的资源和地址空间,所以线程的地址空间和栈是放在内存地址空间的?
- 线程与进程的区别:进程与线程的区别 - SegmentFault 思否
进程
一、首先是书本介绍进程的重点句:
(1)“进程是系统进行资源分配和调度一个独立单位 ”
(2)“在操作系统中运行的exe程序可以理解为一个进程”
(3)“程序是指令序列,这些指令可以让CPU完成指定的任务 ,.java程序经编译过后形成的 .class文件,在windows中启动一个Java虚拟机相当于创建了一个进程,在虚拟机中加载class文件并运行”
(4)“每执行一次main()方法就创建了一个JVM虚拟机进程”
进程的介绍到此为止对于我们的帮助还远远不够,只要稍加思考便会发现自己无法解释许多问题:自己的计算机一次最多执行多少个进程?一个CPU一次只能执行一个进程吗?那我们开了这么多程序(浏览器、网易云音乐、idea等等)不早就超出4核8核了吗?
解答: (65条消息) 操作系统面试题:单核cpu同一时刻能处理多少个进程_我是方小磊的博客-CSDN博客_一个cpu可以开多少个进程 在看答案之前,自己先试着给出答案。同时该文章内结合CPU核数对线程的实际执行效果为并发还是并行给出了答案。
怎么看自己的CPU是几核的。 任务管理器--性能 右下角 (我的电脑是4核)
回到进程:
(1)如果我们的CPU调度的最小单位是进程(一件事只能一个人做),在任务中某部分出现严重阻塞情况时,剩余部分不得不等待,那么此时的CPU效率将会大大降低,因为这意味着cpu占用着资源却无事可做。
正如博客中所说的,缺陷,主要集中在两点:
(i).进程只能在同一时间干一件事情,如果想同时干两件事或多件事情,进程就无能为力了。
(2).进程在执行的过程中如果由于某种原因阻塞了,例如等待输入,整个进程就会挂起,其他与输入无关的工作也必须等待输入结束后才能顺序执行。
线程
二、然后是书本介绍多线程
在描述多线程优点时,书中拿了单任务运行环境与多任务运行环境的对比,以“多任务的特点是在同一时间可以执行多个任务,这也是多线程的优点,使用多线程也就是在使用异步”这句话草草结尾,可能会给读者留下了多线程是处理多任务的错误印象,不够严谨。线程是一个任务中的一个执行体,并且多线程不一定是处理多任务,也可以是处理单个任务比如计算。并且异步这个概念没有解释清楚。
本人认为的更好的解释:
(65条消息) CPU,多核,多线程,并发,并行,计算效率_rs_gis的博客-CSDN博客_cpu并行计算
- 清楚多线程与高并发的关系:多线程为什么能处理高并发问题
- 并发一定能提高效率吗?
- 并发/并行与CPU核数之间的联系?
并发、并行
并发和并行都是完成多任务更加高效的方式
并发:交替做不同事情的能力,不同的代码块交替执行。通过CPU调度算法,让用户看上去同时执行,实际上CPU操作层面不是真正的同时。
并行:同时做不同事情的能力,不同的代码块同时执行。多个CPU实例或多台机器同时执行一段处理逻辑,是真正的同时。
同步、异步、阻塞非阻塞
“同步和异步关注的是消息通信机制” 但是不能和阻塞与非阻塞混为一谈,阻塞和非阻塞关注的是状态,是线程执行时的状态
同步和异步关注的是线程执行的方法,是主动等待和主动不等待。 同步(Synchronous)和异步(Asynchronous) - myCpC - 博客园 (cnblogs.com)
阻塞和非阻塞 强调的是程序在等待调用结果(消息,返回值)时的状态. 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 对于同步调用来说,很多时候当前线程还是激活的状态,只是从逻辑上当前函数没有返回而已,即同步等待时什么都不干,白白占用着资源。
同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。而异步则是相反,"调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用
多线程与并发
(66条消息) CPU,多核,多线程,并发,并行,计算效率_rs_gis的博客-CSDN博客_cpu并行计算
什么时候该用多线程
多线程:到底什么时候该用多线程 - 悟空悟不空 - 博客园 (cnblogs.com)
博客中的重要思想:
(1)线程是为了解决等的问题(等待程序响应),也可以解决慢的问题(提高性能)
书本上的描述是,
(1)存在阻塞现象,可以根据实际情况使用多线程提高运行效率。小白如果看到这里可能会有点懵,换个角度理解就豁然开朗了,让可能发生阻塞的任务在后台运行,不影响其他的功能,就是多线程的好处,即我们采用(异步/非阻塞模式)的方法。
(2)依赖,可以将大任务分割成不同的小任务(切片),这一步我的理解是“CPU轮流执行可能可以产生并行效果,如果不用多线程那么必然是单任务单CPU,多线程则可能是多任务多CPU”,也有博主说是CPU执行速度过快,其他部分如数据传输跟不上,轮流执行可以提高效率。
关于理解的部分我们就先到这里,接下来用具体的实例(来自上述博客)学习。
利用Java.swing设计刷新按钮时难免会遇到的问题,“刷新假死”。 产生的原因是我们希望的阻塞过程是绘制的过程,但是实际上swing中刷新和绘制处于同一个线程,这种写法会同时阻塞画面
b.addActionListener(e -> {
for(int i=1;i<=10;i++){
tf.setText("i:"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
此处优化可以是将点击事件的刷新过程封装到线程中去,将每次的点击都视作创建一个线程,使用异步的方式,不让绘制的阻塞影响到刷新。
坦克和子弹的设:解决方案是将所有的阻塞Thread.sleep(1000)都封装到一个线程中去,不影响绘制和刷新。(代码部分还需重新理解)
总结:swing中的绘制阻塞和坦克中的延时阻塞(希望的),以及I/O中的阻塞(不希望的)要分开理解,对于不希望的阻塞,我们可以采用多线程异步。对于设计的延时阻塞,