个人主页Dawn黎明开始

系列专栏Java
每日一句身在井隅,心向阳光,眼里有诗,自在远方

欢迎大家:关注+点赞+评论+收藏⭐️


文章目录

一.多线程

前言

1.1进程

1.1.1什么是进程?

1.1.2多进程有什么意义呢?

1.2线程

1.2.1什么是线程呢?

1.2.2多线程有什么意义呢?

1.3Java程序的运行原理

1.3.1原理

1.3.2思考题

二.多线程的三种创建方法

2.1.继承Thread类

2.1.1步骤

2.1.2实例练习

2.1.3思考

2.2实现Runnable接口

2.2.1步骤

2.2.2实例练习

2.3实现Callable接口

2.2.1步骤

2.2.2实例练习

2.4三种方法的对比分析

2.4.1分析

2.4.2优点

2.4.3说明


一.多线程

前言

要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。

1.1进程

1.1.1什么是进程” />

通过任务管理器我们就看到了进程的存在,而通过观察,我们发现只有运行的程序才会出现进程。

1.1.2多进程有什么意义呢?

单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。

举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。

总结

现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。多进程可以提高CPU的使用率(前提:单CPU系统)。

思考

一边玩游戏,一边听音乐是同时进行的吗?

不是。因为单CPU在某一个时间点上只能做一件事情,在计算机中,所有的应用程序都是由CPU执行的,对于一个CPU而言,在某个时间点只能运行一个程序,也就是说只能执行一个进程,操作系统会为每一个进程分配一段有限的CPU使用时间,CPU在这段时间中执行某个进程,然后会在下一段时间切换到另一个进程中去执行。

而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。

总结

(1).在多任务操作系统中,表面上看是支持进程并发执行的,例如可以一边听音乐一边聊天,但实际上这些进程并不是在同一时刻运行的。

(2).由于CPU运行速度非常快,能在极短的时间内在不同的进程之间进行切换,所以给人以同时执行多个程序的感觉。

1.2线程

1.2.1什么是线程呢?

定义

多任务操作系统中,每个运行的程序都是一个进程,用来执行不同的任务,而在一个进程中还可以有多个执行单元同时运行,来同时完成一个或多个程序任务,这些执行单元可以看做程序执行的一条条线索,被称为线程。

说明

(1).单线程都是按照调用顺序依次往下执行,没有出现多段程序代码交替运行的效果,而多线程程序在运行时,每个线程之间都是独立的,它们可以并发执行

(2).多线程可以充分利用CUP资源,进一步提升程序执行效率

(3).多线程看似是同时并发执行的,其实不然,它们和进程一样,也是由CPU控制并轮流执行的,只不过CPU运行速度非常快,故而给人同时执行的感觉。

多线程举例

扫雷程序、百度云盘、百度网盘下载

1.2.2多线程有什么意义呢” />多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。

(1).程序的执行其实都是在抢CPU的资源,CPU的执行权 。

(2).多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。

(3).我们是不能确定哪一个线程能够在哪个时刻抢到,所以线程的执行具有随机性

1.3Java程序的运行原理

1.3.1原理

Java命令去启动JVM,JVM会启动一个进程,该进程会创建了一个主线程调用main方法。

1.3.2思考题

JVM的启动是多线程的吗?

是多线程的,原因是:垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。

二.多线程的三种创建方法

2.1.继承Thread类

如何获取线程对象的名称呢" />2.1.1步骤 

A:自定义类MyThread继承Thread类。

B:MyThread类里面重写run()

C:创建对象

D:启动线程

2.1.2实例练习

实例练习1

代码如下

package Process;public class MyThread extends Thread {@Overridepublic void run() {//多线程的任务for(int i=0;i<200;i++) {System.out.println(i);}}}
package Process;public class MyThreadDemo {public static void main(String[] args) {// 创建两个线程对象MyThread my1 = new MyThread();MyThread my2 = new MyThread();my1.start();my2.start();}}

运行结果

实例练习2

代码如下

package Process;public class MyThread1 extends Thread {public MyThread1() {super();}public MyThread1(String name) {super(name);}@Overridepublic void run() {//多线程的任务for(int i=0;i<200;i++) {System.out.println(this.getName()+":"+i);}}}
package Process;public class MyThreadDemo1 {// 创建两个线程对象public static void main(String[] args) {MyThread1 my3 = new MyThread1("张三");MyThread1 my4 = new MyThread1("李四");my3.start();my4.start();for(int i=0;i<20;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}

运行结果

2.1.3思考

思考:调用run()方法为什么是单线程的呢" />run()和start()的区别?

run():仅仅是封装被线程执行的代码,直接调用是普通方法

start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

2.2实现Runnable接口

2.2.1步骤

A:自定义类MyRunnable实现Runnable接口

B:重写run()方法

C:创建MyRunnable类的对象

D:创建Thread类的对象,并把C步骤的对象作为构造参数传递

E:启动线程(Thread类的对象)

思考:有了方法1,为什么还来一个方法2呢?

(1).可以避免由于Java单继承带来的局限性

(2).适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

2.2.2实例练习

代码如下

package Process;public class MyRunnable implements Runnable {@Overridepublic void run() {for(int i=0;i<200;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}
package Process;public class MyRunnableDemo {public static void main(String[] args) {// TODO Auto-generated method stubMyRunnable mr =new MyRunnable();Thread t1 =new Thread(mr,"王五");t1.start();Thread t2=new Thread(mr,"赵六");t2.start();//简洁方法//new Thread(new MyRunnable(),"a").start();//new Thread(new MyRunnable(),"b").start();}}

运行结果

2.3实现Callable接口

2.2.1步骤

A:自定义类MyCallable实现Callable接口

B:重写call()方法

C:创建MyCallable类的对象

D:创建FutureTask类的对象,并把C步骤的对象作为构造参数传递

E:创建Thread类的对象,并把D步骤的对象作为构造参数传递

F:启动线程(Thread类的对象)

与两种方式的区别:

(1).该方式的线程执行结束返回一个值

(2).可以通过FutureTask类的对象的get()方法接受返回值

2.2.2实例练习

代码如下

package Process;import java.util.concurrent.Callable;public class MyCallable implements Callable {@Overridepublic Object call() throws Exception {int sum=0;for(int i=0;i<=10;i++) {System.out.println(Thread.currentThread().getName()+":"+i);sum+=i;}return sum;}}
package Process;import java.util.concurrent.FutureTask;public class MyCallableDemo {public static void main(String[] args) throws Exception {//2、创建Callable接口的实现类对象MyCallable MyThread1=newMyCallable();//3、使用 FutureTask封装Callable接口FutureTask ft1 =newFutureTask(MyThread1);//4、使用Thread(Runable target,String name)构造方法创建线程对象Thread thread1 =new Thread(ft1,"线程A");//5、调用线程对象得start()方法启动线程thread1.start();//创建并启动另一个线程FutureTask ft2 =newFutureTask(MyThread1);Thread thread2 =new Thread(ft2,"线程B");thread2.start();//可以通过FutureTask对象得方法管理返回值System.out.println("线程A的返回值:"+ft1.get());System.out.println("线程A的返回值:"+ft2.get());}}

运行结果

2.4三种方法的对比分析

2.4.1分析

(1).继承Thread类是最简单和直接的方式,但限制了类的扩展性。

(2).实现Runnable接口提供了更好的灵活性,使得多个线程可以共享一个任务。

(3).实现Callable接口则更适合需要获取线程执行结果的情况,可以更方便地处理线程执行后的返回值或异常。

2.4.2优点

通过实现Runnable接口(或者Callable接口)相对于继承Thread类实现多线程来说

(1).适合多个线程去处理同一个共享资源的情况。

(2).可以避免Java单继承带来的局限性。

2.4.3说明

事实上,实际开发中大部分的多线程应用都会采用Runnable接口或者Callable接口的方式实现多线程。


建议大家亲自动手操作,学编程,多实践练习是提升编程技能的必经之路。

欢迎大家在评论区进行讨论和指正!

Copyright © maxssl.com 版权所有 浙ICP备2022011180号