【JavaEE】多线程编程引入——认识Thread类
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!希望本文内容能帮到你!
目录
引入:
一:Thread类
1:Thread类可以直接调用
2:run方法
(1)主线程:
(2)Thread创建实例
3:start方法
4:内核
(1)用户态:
(2)内核态:
(3)危害
二:线程的随机调度
1:随机调度
(1)调度器
(2)“抢占式执行”
(3)打印结果分析:
2:jconsole工具
3:sleep方法
4:异常捕获
引入:
通过前面的学习,我们了解到现在主流的编程方式为,多进程编程和多线程编程
在java中并不推荐使用多进程编程这种方式,因为很多跟多进程编程有关的api,Java标准库中都没有提供。
但是多线程不一样,操作系统给我们提供了很多api,但是这些api都是C++、C实现的,api风格也是C++/C的。所以JVM就把这些api给封装成JAVA代码能使用的api,咱们就可以直接使用了嘿嘿~
补充:跑起来的程序叫进程,没跑起来的程序(.exe可执行文件)
一:Thread类
class MyThread extends Thread{
@Override
public void run() {
System.out.println("这就是进入该线程的入口");
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
//根据类,创建实例,线程实例才是真正的线程
//一般用向上转型的写法
Thread t = new MyThread();
t.start();
}
}
1:Thread类可以直接调用
不需要导包,因为它包含在java一个很特殊的包里java.lang(可以想象你写String,Int这些类的时候有没有导包,没有吧,Thread也是一样的)
2:run方法
(1)主线程:
一个进程中至少有一个线程,进程中的第一个线程就叫做“主线程”,上面图片中的main方法就是主线程
(2)Thread创建实例
重写的run方法是另一个线程的入口,相当于main函数入口,根据Thread类创建实例,线程实例才是真正的线程,一般我们用Thread向上转型的写法,不用MyTthread创建实例,
注:方法的重写本质上是让你可以对现有的类进行扩展
3:start方法
创建完实例之后,调用Thread中的start方法,才会真正的调用系统的api,在系统的内核中创建出线程
注:当引入线程后,代码就可以同时具备多个执行流了
4:内核
操作系统 = 内核 + 其他配套的应用程序。内核是操作系统核心模块。
我们把把空间分为内核空间(内核态),和用户空间(用户态)
(1)用户态:
当一些应用程序(像咱们常用的qq,微信,浏览器,爱奇艺啥的)在执行一些常规操作的时候,就会由操作系统提供一些已经封装好的api,然后这些应用程序调用api在用户空间内完成一些常规操作,这些操作可以视为“合法操作”并不会对软硬件造成什么潜在危害。
(2)内核态:
但是有些程序需要针对软硬件资源进行操作的时候(这些操作是比较危险的),就需要向系统获取更高权限,进入内核空间去调用相应的api
(3)危害
如果让程序直接操作软硬件,出个什么bug之类的,可能会把硬件直接干冒烟这种情况(像我们学51单片机的时候,经常出现芯片被烧的情况)
二:线程的随机调度
class MyThread2 extends Thread{
@Override
public void run() {
while (true){
System.out.println("run方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Thread t = new MyThread2();
t.start();
while (true){
System.out.println("main方法");//同时打印
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
前引:我们运行上述代码,观察这两个线程(main,run)究竟谁先打印
<——————打印结果
1:随机调度
(1)调度器
上述代码是两个死循环,但是代码执行的时候并没有报错,而是在无限打印(结果如上),说明这是两个独立的执行流,互不干扰,但是这些线程执行的顺序是不一样的(从打印结果上可以看出来),这里涉及到操作系统中,有一个在内核当中的模块——“调度器”,它的实现方式类似于“随机调度”的效果
(2)“抢占式执行”
一个线程什么时候被调度到cpu上执行是不确定的
一个线程什么时候从cpu上下来,也是不确定的
这在我前面写过的文章cpu的调度有提及,线程的安全性问题,当前主流的操作系统都是“抢占式执行”
(3)打印结果分析:
可以看出先打印run的次数是比较多的。在前面我们学过,线程的引入是为了解决“进程频繁创建和销毁带来的资源浪费”这一问题的,同一个进程中,有第一个线程创建后,后面的线程创建所耗费的资源就会大大降低。
这里同样是如此,main函数作为这个进程中的主线程,第一个创建所耗费的资源是最多的,但是也是最先被打印的,后续while循环中,run第一次出现的次数才更多的
注:线程的创建也是需要耗费资源的,第一个线程最大,后续的比较少,但不是没有
2:jconsole工具
查看线程信息的工具
代码出问题可以在这查看
3:sleep方法
(1)我们引入while循环达到多次实验的目次,sleep方法是为了避免打印过快而引入的
(2)C语言中是大写的Sleep,JAVA中对其进行了封装,所以我们用的是.sleep(),括号中是填毫秒数
(3)在写sleep方法时会报异常:这是一个受查异常,必须要捕捉,有两种捕捉方式——try catch / throw
(interrupted)被打断
在sleep1000ms这个过程中,可能会被打断,就会报异常
4:异常捕获
在main方法中既可以使用try catch 也可以使用 throw ,但是run方法中只能使用try catch这是为什么呢?
重写的run方法为子类方法,因为父类run方法并没有抛throw异常,那子类也不能写,只能是保留@Override这个标签