文章目录
前言 ´・ᴗ・`
多线程 基础自然是进程 线程
- 本节将会帮助你了解…
- 进程的理解
- 线程的理解 进程与线程的关系
- Thread实现 Run Start应用
- Thread实现图片下载
进程 一段静态程序 动态执行的过程
定义 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。
说白了
- 静态的代码经过编译(或者解释),
- 转为机器码
- 操作系统分配系统资源来运行程序,比如内存空间
- 也就是把机器码(可运行代码)放到内存里面
- 除了存可运行代码 很重要的是保存运行结果 因此很多值处在内存的堆栈里边 并不和代码放一起
- 如果得到CPU的青睐 这个内存的程序就有幸能够在CPU中 被CPU计算
- 之前所说的堆栈中的初始值也会被运输到CPU的寄存器中 进行相应计算
- 如果CPU另寻新欢 想抛弃这个进程寻求下一个进程的爱(好吧就是为下一个进程计算)
- 那CPU也会负责任的把已经计算的结果 放到刚刚进程(前男友?)空间的内存堆栈里边
一个计算机系统进程包括(或者说“拥有”)下列数据:
程序的可运行机器码在存储器的映像
分配到的存储器(通常包括虚拟内存的一个区域)
存储器的内容包括可运行代码
特定于进程的数据(输入、输出)
调用堆栈、堆栈(用于保存运行时运数中途产生的数据)
分配给该进程的资源的操作系统描述符,诸如文件描述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。
安全特性,诸如进程拥有者和进程的权限集(可以容许的操作)
处理器状态(内文),诸如寄存器内容、物理存储器寻址等。当进程正在运行时,状态通常储存在寄存器,其他情况在存储器。
可能你还是不太理解另寻新欢的过程,换言之,这就是进程的切换
进程的切换
进行进程切换 就是从正在运行的进程中收回处理器,然后再使待运行(所谓的就绪状态)进程来占用处理器。
换言之 其实可以理解为CPU决定要为哪个进程计算,就好像女神决定要为那个男士服务一样,如果这个男士不够格,也就是进程的优先级不够 那么CPU女王就会另寻新欢~ 之前处于就绪态的进程(备胎)就会上位。(当然这里 并非CPU做的决策 CPU只能计算 还有别的部分负责鉴定进程优先级 这里你可以这样yy理解)
所以之前的进程(前男友),存放在处理器的寄存器中的中间数据该怎么办?因为处理器的寄存器要腾出来让其他进程使用(让给下一位进程男士~)。那么被中止运行进程的中间数据存在何处?前男友进程的私有堆栈。
因此 进程男士,占用处理器女神,第一步就是把进程存放在私有堆栈中的数据,也就是前一次进程男士被中止(被抛弃)时的中间数据,再插入(复制移动MOV)到处理器女神的寄存器中去,
并把待运行进程的断点 也就是上次没计算完 没做完的部分 送入处理器的程序指针PC,于是处理器开始接着上次的部分运行,接着上次的部分做。
总结起来 切换包含了清理旧进程(该拿走的赶紧拿走 本CPU不存)以及接纳新进程(把数据和PC指针都注入进来吧~),换言之,也就是女神抛弃前男友和接入新男友两个部分
进程的状态
进程执行时的间断性,决定了进程可能具有多种状态。就类似男士是单身,还是已经有女朋友,还是被暂时抛弃,这几种情感状态一样~
1)就绪状态(Ready)—— 单身
进程已获得除处理器外的所需资源,等待处理器女神;只要分配了处理器,进程就可执行。
注意 就绪进程具有不同的优先级,会形成队列,就类似女神会给她的备胎们排序,对于进程而言,当一个进程由于时间片用完而进入就绪状态时,就会排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,则会排入高优先级队列。
2)运行状态(Running)——已有女朋友
进程占用处理器资源;
当然 如果 在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程 类似CPU会自己干自己的2333。
3)阻塞状态(Blocked)——不举
由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。所以遭到CPU抛弃,CPU将另寻新欢,切换到别的进程。
进程与线程
这里可以参考阮一峰大佬(竟然与大佬同姓)的文章,讲比较生动有趣
线程的应用 实现之一 Thread
说白了 由于进程的创建花费的资源太多 一般采用多线程的形式 我们java不也是main线程(用户线程) gc线程(守护线程)整起来的的嘛:)
我们有三种方式来实现线程
- Thread继承
- Runnable接口实现
- Callable接口实现
先来聊聊Thread(基础)
我们直观感受多线程 跑个小demo就好:
Run函数与Start函数
经典 区别就是 run函数就是直接调用(传统执行方式) 而start则隐含多线程的效果
对比一下,我们把叫miao(类名为thread_first)作为一个线程,叫wang作为主线程,然后测试如下代码, 在主线程运行的同时,使用thread_first.start()
代码如下:
package com.base.threadlearning;
public class thread_first extends Thread{
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("miao"+i);
}
}
public static void main(String[] args) {
thread_first thread_first = new thread_first();
thread_first.start();
for (int i = 0; i < 20; i++) {
System.out.println("wang"+i);
}
}
}
如果像这样 我们用start来执行线程thread_first 那么可以实现与主线程交替执行的多线程效果:
wang0
miao0
wang1
miao1
wang2
wang3
miao2
wang4
miao3
wang5
miao4
wang6
miao5
wang7
miao6
wang8
miao7
wang9
miao8
wang10
miao9
wang11
miao10
wang12
miao11
wang13
miao12
wang14
miao13
wang15
miao14
wang16
miao15
wang17
miao16
miao17
miao18
wang18
miao19
wang19
如果使用run 那就是单纯执行而已 如下:
miao0
miao1
miao2
miao3
miao4
miao5
miao6
miao7
miao8
miao9
miao10
miao11
miao12
miao13
miao14
miao15
miao16
miao17
miao18
miao19
wang0
wang1
wang2
wang3
wang4
wang5
wang6
wang7
wang8
wang9
wang10
wang11
wang12
wang13
wang14
wang15
wang16
wang17
wang18
wang19
可见thread_first先执行了
Thread实现图片下载
前面那个例子有点尬 也就是直观体验一下 现在来个稍微有点用的例子 下载图片。首先实现一下,下载功能的对象
package com.base.threadlearning.download_pic;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class Downloader {
public void download(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("download failed");
}
}
}
然后我们来写Thread 的demo 验证一下两个问题
1 先start的线程一定先执行嘛?如果不是 那是谁决定顺序呢?
2 效率真的有提升吗?(时间减少)
我们编写demo 如下
package com.base.threadlearning.download_pic;
public class Thread_download_pic extends Thread{
private String url;
private String name;
public Thread_download_pic(String url, String name){
this.name = name;
this.url = url;
}
@Override
public void run(){
Downloader downloader = new Downloader();
downloader.download(url,name);
System.out.println("download "+name+" done");
}
public static void main(String[] args) {
String _url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1604058853499&di=65a5ee6cea043252fd2385db8658e11f&imgtype=0&src=http%3A%2F%2Fcdn.duitang.com%2Fuploads%2Fitem%2F201410%2F17%2F20141017162807_4JuXA.thumb.700_0.jpeg";
String _name = "D:\\Users\\Ryan\\IdeaProjects\\jvm_base_knowledge\\src\\main\\java\\com\\base\\threadlearning\\download_pic\\saber\\";
Thread_download_pic thread_download_pic_1 = new Thread_download_pic(_url,_name+"1.jpg");
Thread_download_pic thread_download_pic_2 = new Thread_download_pic(_url,_name+"2.jpg");
Thread_download_pic thread_download_pic_3 = new Thread_download_pic(_url,_name+"3.jpg");
thread_download_pic_1.start();
thread_download_pic_2.start();
thread_download_pic_3.start();
}
}
下载的名称与路径_name 你们可以自定义 我这里也不隐藏我的绝对路径了 反正黑客破解了也找不到什么值钱的东西2333:)
这里注意 我下载器这个类对象Downloader是单独拉出来一个文件放置的
毕竟只要和main所在类Thread_download_pic 同一目录 可以直接调用 当然你把它放在里面当成内部类也可以 我是为了方便共同调用 抽离出来而已(我们后面还要用Runnnable Callable来实现这个demo)
目录结构如图:
然后我们运行 可以明确 先start与后start确实不影响线程执行的顺序
实际上是CPU轮询线程 CPU决定线程执行的先后 但是放心 谁先执行都一样 因为CPU频率太高 几乎可以看成同时执行,当然 这与真正的并行还相差甚远,尤其没有耗时操作的时候,这类单核CPU跑多线程和单线程效果几乎相同,但是我们这个应用因为有网络请求这种耗时操作 故具有优化的空间
如果是多核CPU那就真正实现了完全或者部分多线程 不过。。核数往往不太可能超过线程数
总结 ´◡`
下一节 Java 从多线程到并发编程(二)——Runnable Callable
- 本文专栏
- 我的其他专栏 希望能够帮到你 ( •̀ ω •́ )✧
- 谢谢大佬支持! 萌新有礼了:)