- 了解掌握多线程的意义?
- 重点掌握多线程的创建方式?如何使用?
多线程概述
操作系统分类
单任务处理
一个任务完成后才能进行下一个任务。
-- 缺点:长时间运行的任务会阻止其他任务执行,有Bug造成无限循环,会使整个计算机停止工作。
多任务处理:CPU分时操作,每个任务看似同时运行。
-- OS将时间划分为很多时间片段(时间片),尽可能均匀分配给正在运行的程序,获取时间片的程序得以执行,其他则等待。而CPU则在这些程序上来回切换。实际所有程序都是走走停停,由于切换频密,看似都在运行,这种现象叫并发,但不是绝对意义上的“同时”发生。
进程
应用程序的一个运行实例,包含程序所需资源的内存区域,是操作系统进行资源分配的单元,进程隔离了正在执行的不同程序。
优点:进程间相互独立,互不影响。
Java程序的运行过程
- 通过eclipse(java命令)运行一个Java程序,java命令会启动JVM(java虚拟机),等于启动了一个应用程序,也就是启动了一个进程。
- 该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法,所以 main 方法运行在主线程中。在此之前的所有程序代码都是按照顺序依次调用的,都是单线程的。
如果希望程序中实现多段程序代码交替执行的效果,则需要创建多线程程序。
为什么要使用多线程?
- 单线程程序执行时都只有一条顺序执行流,如果程序执行某行代码时遇到了阻塞(比如:抛异常),那么程序的运行将会停滞在这一行,其他代码将会无法执行!
- 这就像去银行办理业务,只有1个业务窗口(单线程),所有的客户都需要在一个窗口排队办理业务,如果业务员在为某一个客户办理业务时,花费了很长时间,那么将会导致后面的客户等待很长时间,这样处理业务的效率也是非常低的。
- 但如果银行为了提高效率,同时开放了5个窗口(多线程),客户可以分布在这5个窗口分别办理业务,即使某一个窗口在为个别客户办理业务时花费了很长时间,但不影响其他窗口办理业务的进度。
多线程理解起来其实非常简单:
- 单线程的程序只有一个顺序执行流。
- 多线程的程序则可以包括多个顺序执行流,多个顺序流之间互不干扰。
[并行]和[并发]的区别
1)并行执行指在同一时刻,有多条指令在多个处理器上同时执行;
2)并发执行指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
多线程的特性
随机性
多线程的程序在执行时,在某一时间点具体执行哪个线程是不确定的,可以确定的是某一个时间点只有一个线程在执行(单核CPU)。
虽然我们感觉这些线程像是在同时运行,实际上是因为CPU在快速切换轮流执行这些线程,由于切换速度是纳秒级别的,所以我们感觉不到。
如何实现多线程
由于线程是依赖进程存在的,因此首先需要创建一个进程,但进程是操作系统创建的,而Java程序是不能直接调用系统功能的。但Java可以去调用C或C++写好的程序去创建进程从而实现多线程程序。
在Java中要想实现多线程操作有两种方式,一种是继承Thread类,另一种是实现Runnable接口。接下来针对这两种创建多线程的方式分别进行讲解。
1. 继承Thread类
Thread类概述
Thread类是在java.lang包中定义的类
JavaSE规范中规定,一个类只要继承了Thread类,此类就是多线程的子类。
在Thread子类中,必须重写该类的run()方法,此方法为线程的主体。
- 通过继承Thread类创建线程
接下来通过案例演示<通过继承Thread类的方式实现多线程>
下面创建线程,并测试主线程和创建的新线程(子线程)交替执行的效果
代码实现如下:
package thread;
/**
* 多线程
* 多线程可以并发执行多个任务
* 线程的第一种创建方式
* 继承Thread类,并且重写其中的run方法,在run方法中定义需要并发执行的任务代码
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//4.创建线程类的实例
Thread t1 = new MyThread01();
Thread t2 = new MyThread02();
//5.启动线程要调用start()方法
/*
* 注意:启动线程是调用 start方法,并非直接调用run方法,当调用start方法时,
* 线程会纳入线程调度器中统一被管理,一旦线程分配CPU的时间片,
* 于是就开始自动执行run方法
*/
t1.start();
t2.start();
}
}
/*
* 这种创建线程的方式的优点是: 结构简单,便于匿名内部类创建
* 缺点:
* 1.java是单继承,也就是使用这种方式,当前类就不能再继承别的类了,无法复用其他方法,
* 对于实际开发会有很多的不便
* 2.定义线程时,重写了run方法,直接将任务定义在了线程里,导致线程与任务存在必然的耦合关系,
* 不利于线程的重用
*/
//1.创建一个线程类,继承Thread类
class MyThread01 extends Thread{
//2.重写Thread类中的run方法
@Override //可以编译器自动校验重写的方法体是否符合语法
public void run() {
//3.run方法中要书写线程执行的代码内容
for(int i=0;i<1000;i++){
System.out.println("第"+i+"次说:你是谁啊!?");
}
}
}
class MyThread02 extends Thread{
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("第"+i+"次说:开门,查水表的!!!");
}
}
}
从上面的运行结果可以看出,main方法(主线程)和run方法(子线程)中的两个for循环中的输出语句交替执行了,说明通过集成Thread类实现了多线程。
(如果没有测试出主线程和子线程交替执行的效果,可以多测试几次!)
2. 实现Runnable接口
Runnable接口概述
通过继承Thread类实现了多线程,但是这种方式有一定的局限性。因为Java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,例如猫类Cat继承了动物类Animal,就无法通过继承Thread类实现多线程。
为了克服这种弊端,在Thread类中提供了两个构造方法:
public Thread(Runnable target)
public Thread(Runnable target,String name)
这两个构造方法都可以接收Runnable的子类实例对象,这样创建的线程将调用实现了Runnable接口类中的run()方法作为运行代码,而不需要调用Thread类的run()方法,所以就可以依靠Runnable接口的实现类启动多线程。
- 通过实现Runnable接口实现多线程
接下来通过案例演示<通过实现Runnable接口的方式实现多线程>
下面创建线程,并测试主线程和创建的新线程(子线程)交替执行的效果
代码实现如下:
package thread;
/**
* 第二种创建线程的方式:
* 实现Runnable接口,定义线程任务
*/
public class ThreadDemo2 {
public static void main(String[] args) {
//4.实例化线程要实现的任务
Runnable r1 = new MyRunnable01();
Runnable r2 = new MyRunnable02();
//5.创建具体的线程实例,接载具体线程要实现的任务
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
/*
* 实现Runnable接口的缺点: 结构稍微复杂一点
* 优点:
* 1.避免继承Thread类单继承的局限性,类是单继承多实现
* 2.降低了线程对象和线程任务的耦合性
*/
//1.创建一个类,实现Runnable接口
class MyRunnable01 implements Runnable{
//2.实现Runnable接口的run方法
@Override
public void run() {
//3.写线程要实现的任务
for (int i=0;i<1000;i++){
System.out.println("第"+i+"次说:hello!姐~");
}
}
}
class MyRunnable02 implements Runnable{
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("第"+i+"次说:来了~老弟~");
}
}
}
从上面的运行结果可以看出,main方法(主线程)和run方法(子线程)中的两个for循环中的输出语句交替执行了,说明实现Runnable接口同样也实现了多线程。
总结
1. 多线程的意义是什么?
答:单线程程序执行时都只有一条顺序执行流,如果程序执行某行代码时遇到了阻塞(比如:抛异常),那么程序的运行将会停滞在这一行,其他代码将会无法执行!所以可以通过开启多个线程,让某些代码的执行可以实现并行的情况。
2. 多线程的创建方式?如何使用?
答:在Java中要想实现多线程操作有两种方式:① 实现类继承Thread类,然后通过重写run方法实现该线程的任务逻辑,最后通过start方法调用开启;
② 实现Runnable接口,然后通过重写run方法实现该线程的任务逻辑,最后通过start方法调用开启。
上篇文章:入门Java编程的知识点—>异常(day14)-CSDN博客https://blog.csdn.net/Z0412_J0103/article/details/141552392下篇文章:入门Java编程的知识点—>线程安全(day16)-CSDN博客https://blog.csdn.net/Z0412_J0103/article/details/141553816