news 2026/3/31 13:45:31

4.3.多线程JUC-多线程的实现方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
4.3.多线程JUC-多线程的实现方式

一.多线程共有3种实现方式:


二.第一种实现方式:继承Thread类的方式进行实现

1.Thread类详解:

如上图,其中解释到线程是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程,意思是Thread类就表示Java里的一个线程,如果想要拥有一条线程的话,就可以创建Thread类对象并开启它即可。

具体实现步骤如下:

多线程的第一种启动方式:
* 步骤一:自己定义一个类继承Thread类
* (Thread类Java已经定义好,表示线程,因此自己定义的类也表示线程,因为是Thread类的子类)
* 步骤二:重写run方法
* 步骤三:创建子类的对象并启动线程

2.实例:

如上图,

该创建新执行线程的方法是将类声明为Thread的子类。该子类应重写Thread类的run方法。接下来可以分配并启动该子类的实例即接下来可以创建子类的对象。例如,计算大于某一规定值的质数的线程可以写成如上图的代码 ,调用start方法来开启线程后就会自动去找对应的run方法,去执行run方法里的代码,

再举一个例子:

package com.itheima.a01threadcase1; public class MyThread extends Thread{ //重写run方法,并书写线程要执行的代码 @Override public void run() { /*开始书写线程要执行的代码*/ for (int i = 0; i < 5; i++) { System.out.println("Hello World"); } } }
package com.itheima.a01threadcase1; public class ThreadDemo { public static void main(String[] args) { /* * 多线程的第一种启动方式: * 步骤一:自己定义一个类继承Thread类 * (Thread类Java已经定义好,表示线程,因此自己定义的类也表示线程,因为是Thread类的子类) * 步骤二:重写run方法 * 步骤三:创建子类的对象并启动线程 * * */ //1.创建Thread类的子类MyThread的对象 MyThread t1=new MyThread(); //2.启动线程 /*注:启动线程不能直接调用run方法, 如果直接调用run方法,仅仅是调用了一个方法,与之前的单线程程序是一样的, 正确的操作是调用start方法启动线程,start方法才表示开启线程, 之后也就无需调用run方法了,因为调用start方法后Java会自动去找对应的run方法,去执行run方法里的代码*/ t1.start(); } }

这里可能会有疑问,上述代码与之前的创建对象并调用方法有什么区别呢?

此时还没有什么区别,因为现在只创建了一个线程对象,

如果现在再创建第二个线程对象,并启动第二个线程对象,如下图:

此时运行上图的程序,就会出现一会儿执行第一条线程t1,一会儿执行第二条线程t2,由于t1和t2执行的代码都是run方法里的内容,而且都是打印Hello World,

所以此时直接运行的话是无法分清谁是哪个线程打印的,

所以在这儿还需要额外的做一些处理,给t1和t2这两个线程分别起一个名字,并在run方法里把对应的名字写上,代码如下:

package com.itheima.a01threadcase1; public class MyThread extends Thread{ //重写run方法,并书写线程要执行的代码 @Override public void run() { /*开始书写线程要执行的代码*/ for (int i = 0; i < 99; i++) { /*这里多循环几次,为了更好的看出进程交替进行, 循环次数少可能看不出,因为循环次数少可能在进程数的最大范围内,可以不交替就同时进行*/ //getName方法用于获取线程的名字,为了区分线程进行过程,子类MyThread可直接调用父类Thread的方法 //哪个线程调用run方法,getName方法获取的就是哪个线程的名字 System.out.println( getName()+"->Hello World"); } } }
package com.itheima.a01threadcase1; public class ThreadDemo { public static void main(String[] args) { /* * 多线程的第一种启动方式: * 步骤一:自己定义一个类继承Thread类 * (Thread类Java已经定义好,表示线程,因此自己定义的类也表示线程,因为是Thread类的子类) * 步骤二:重写run方法 * 步骤三:创建子类的对象并启动线程 * * */ //1.创建Thread类的子类MyThread的对象 MyThread t1=new MyThread(); MyThread t2=new MyThread(); /*给t1和t2分别起一个名字,为了区分两个线程*/ t1.setName("线程1"); t2.setName("线程2"); //2.启动线程 t1.start(); t2.start(); } }

运行结果如下(只截取了一部分):


三.第二种实现方式:实现Runnable接口的方式进行实现

1.实现Runnable接口详解:

如上图,

创建线程的另一种方法是声明实现Runnable接口的类。该类然后实现run方法,也就是说要自己定义一个类,去实现Runnable这个接口,再去重写里面的run方法即可(实现接口就要重写该接口的所有抽象方法,Runnable接口里只有抽象方法run,所以重写run方法),在测试类中首先要创建自己的类,然后再创建Thread线程对象,把自己创建的对象传递给线程对象Thread后再调用start方法启动线程。

具体实现步骤如下:

多线程的第二种启动方式:
* 步骤一:自己定义一个类实现Runnable接口
* 步骤二:重写里面的run方法
* 步骤三:创建自己的类对象
* 步骤四:创建一个Thread类对象,并开启线程

2.例一:只创建一个线程对象

代码:

package com.itheima.a02threadcase2; public class ThreadDemo { public static void main(String[] args) { /* * 多线程的第二种启动方式: * 步骤一:自己定义一个类实现Runnable接口 * 步骤二:重写里面的run方法 * 步骤三:创建自己的类对象 * 步骤四:创建一个Thread类对象,并开启线程 * */ //1.创建自己的类MyRun对象 /*MyRun类此时表示多线程要执行的任务*/ MyRun mr=new MyRun(); //2.创建线程对象 /*当前线程对象要执行mr里的代码,所以把mr传递给线程即可*/ Thread t1=new Thread(mr); //3.开启线程 t1.start(); } }
package com.itheima.a02threadcase2; public class MyRun implements Runnable{ @Override public void run() { /*run是一个抽象方法,实现接口,就要重写该接口的所有抽象方法*/ //书写线程要执行的代码 for (int i = 0; i < 100; i++) { System.out.println("Hello World"); } } }

运行结果:

3.例二:创建多个线程对象

代码:

package com.itheima.a02threadcase2; public class ThreadDemo { public static void main(String[] args) { /* * 多线程的第二种启动方式: * 步骤一:自己定义一个类实现Runnable接口 * 步骤二:重写里面的run方法 * 步骤三:创建自己的类对象 * 步骤四:创建一个Thread类对象,并开启线程 * */ //1.创建自己的类MyRun对象 /*MyRun类此时表示多线程要执行的任务*/ MyRun mr=new MyRun(); //2.创建线程对象 /*当前线程对象要执行mr里的代码,所以把mr传递给线程即可*/ Thread t1=new Thread(mr); Thread t2=new Thread(mr); /*给线程对象设置名字,为了区分哪个线程正在进行*/ t1.setName("线程1"); t2.setName("线程2"); //3.开启线程 t1.start(); t2.start(); } }
package com.itheima.a02threadcase2; public class MyRun implements Runnable{ @Override public void run() { /*run是一个抽象方法(实现接口,就要重写该接口的所有抽象方法)*/ //书写线程要执行的代码 for (int i = 0; i < 100; i++) { /*这里不能直接调用getName方法获取线程名字,因为getName方法位于Thread类,此时MyRun * 只是实现了接口Runnable,Runnable接口没有getName方法,此时该怎么获取线程名字呢? * 技巧:打印之前,先获取到当前线程的对象,可以使用静态方法currentThread*/ //currentThread方法的作用是返回当前正在执行的线程对象 Thread t = Thread.currentThread(); System.out.println(t.getName()+":Hello World"); //比如t1开始线程,调用start方法,自动执行run方法,首先获取到t1的名字线程1,再输出打印 //本例中有两个线程,每个线程循环遍历100个,共200个,只不过线程交替打印 } } }

运行结果:


四.Thread类常用方法:


五.第三种实现方式:利用Callable接口和Future接口方式实现

1.具体实现步骤如下:

多线程的第三种实现方式:
* 为什么要有多线程的第三种实现方式呢?其实是对多线程的第一、二种实现方式的补充->
* 第一种实现方式是继承Thread类并重写run方法,但run方法没有返回值,此时就无法获取多线程运行的结果;
* 同理第二种实现方式是实现了Runnable接口,并重写了抽象方法run,该run方法也没有返回值,
* 因此,如果此时要获取多线程运行的结果,第一、二种实现方式就无法做到,那么该怎么办呢?
* 因此就要用到多线程的第三种实现方式:
* 特点:多线程的第三种实现方式可以获取到多线程的运行结果
* 步骤如下:
* 步骤一:创建一个类如MyCallable实现Callable接口
* 步骤二:重写Callable接口里的所有抽象方法,Callable接口里的抽象方法只有call,因此重写call方法,注:call方法是有返回值的,这个返回值就表示多线程运行的结果
* 步骤三:创建刚才创建的类MyCallable的对象(表示多线程要执行的任务)
* 步骤四:创建FutureTask的对象(作用:管理多线程运行的结果->所以要想获取到多线程运行的结果,直接到创建FutureTask的对象里拿即可),
* 关键在于Future是一个接口,不能直接创建Future的对象,要创建Future的实现类的对象,FutureTask是Future的实现类对象
* 步骤五:创建Thread类的对象(表示线程),并启动线程。

2.实例:

package com.itheima.a03threadcase3; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { /* * 多线程的第三种实现方式: * 为什么要有多线程的第三种实现方式呢?其实是对多线程的第一、二种实现方式的补充-> * 第一种实现方式是继承Thread类并重写run方法,但run方法没有返回值,此时就无法获取多线程运行的结果; * 同理第二种实现方式是实现了Runnable接口,并重写了抽象方法run,该run方法也没有返回值, * 因此,如果此时要获取多线程运行的结果,第一、二种实现方式就无法做到,那么该怎么办呢? * 因此就要用到多线程的第三种实现方式: * 特点:多线程的第三种实现方式可以获取到多线程的运行结果 * 步骤如下: * 步骤一:创建一个类如MyCallable实现Callable接口 * 步骤二:重写Callable接口里的所有抽象方法,Callable接口里的抽象方法只有call,因此重写call方法,注:call方法是有返回值的,这个返回值就表示多线程运行的结果 * 步骤三:创建刚才创建的类MyCallable的对象(表示多线程要执行的任务) * 步骤四:创建FutureTask的对象(作用:管理多线程运行的结果->所以要想获取到多线程运行的结果,直接到创建FutureTask的对象里拿即可), * 关键在于Future是一个接口,不能直接创建Future的对象,要创建Future的实现类的对象,FutureTask是Future的实现类对象 * 步骤五:创建Thread类的对象(表示线程),并启动线程 * */ //1.创建刚才创建的类MyCallable的对象(表示多线程要执行的任务) MyCallable mc=new MyCallable(); //2.创建Future接口的实现类对象FutureTask,用于管理多线程运行的结果 /*FutureTask对象的泛型表示多线程运行的结果,本例中多线程运行的结果为Integer型*/ FutureTask<Integer> ft=new FutureTask<>(mc); /*mc传入,表示现在要用FutureTask对象去管理mc的结果*/ //3.创建线程对象 Thread t1=new Thread(ft); /*该线程要执行ft,所以传入ft*/ //4.启动线程 t1.start(); //5.获取到多线程运行的结果 /*该如何获取呢?刚才说了,FutureTask就是用来管理线程运行结果的, * 所以可以直接用FutureTask类里的get方法来获取线程的结果, * get方法返回值的类型就是线程运行结果的类型,此时是Integer型*/ Integer result = ft.get(); //6.输出 System.out.println(result); } }
package com.itheima.a03threadcase3; import java.util.concurrent.Callable; public class MyCallable implements Callable<Integer> { /*Callable接口是有一个泛型的即Callable<V>,这个泛型V是什么意思呢? * 注:第三种实现方式可以获取到多线程运行的结果,那么这里的泛型V表示线程的结果的类型, * 比如想要开启一个线程,让它求1到100的整数和,最终的结果是整数,所以此时泛型V就可以写Integer*/ //重写抽象方法call,call方法的返回值与泛型V保持一致,因为call方法表示多线程运行的结果,泛型V也表示多线程运行的结果 @Override public Integer call() throws Exception { //求1到100之间的和 int sum=0; for (int i = 1; i <= 100; i++) { sum=sum+i; } return sum; } }

六.总结:

  • 对于多线程的实现方式可以分为两类,本篇中第一、二种实现方式分为一类,无法获取到多线程的结果;第三种实现方式分为一类,此时可以获取到多线程的结果->所以如果要用到多线程的结果,就可以使用第三种实现方式,如果无需多线程的结果,可以选择第一或第二种实现方式
  • 多线程的第一种实现方式与第二种实现方式的区别:第一种实现方式代码比较简单,而且是继承Thread类,所以在子类中可以直接使用Thread类里的方法,但可扩展性比较差,因为Java中是单一继承,此时继承了Thread类,就无法再继承其他的类了;第二种实现方式解决了第一种实现方式的缺点,第二种实现方式扩展性强,因为一个类可以实现多个接口,实现了Runnable接口后还可以实现其他接口,并且还可以继承其他的类,扩展性比较强,但代码相对比较复杂,因为此时只是实现了Runnable接口,并没有线程对象Thread类,也就不能直接使用Thread类中的方法,如果想用Thread类的方法,还需先获取到Thread类的对象,再调用Thread类的方法

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/28 16:23:41

快过年了 , 我就简简单单写一个总结吧 ! | 马年快乐 !

1. 项目正式上线了 书接上回 , 经过一个月的不懈努力 , 我和另一个哥们终于把这个项目 (帮学校做校园宿舍报修系统小程序) 拿下了. 但是这边宿管长这边又出了点状况: 因为这个项目是为学校做公益,没有问宿管长要一分钱. 但是服务器的钱肯定是要宿管长联系学校那边给报销一下…

作者头像 李华
网站建设 2026/3/31 1:39:36

生命的二元张力:弗洛伊德生的本能与死的本能理论解析

生命的二元张力&#xff1a;弗洛伊德生的本能与死的本能理论解析 在经典精神分析理论体系中&#xff0c;西格蒙德弗洛伊德&#xff08;Sigmund Freud&#xff09;晚年提出的“生的本能”&#xff08;Eros&#xff09;与“死的本能”&#xff08;Thanatos&#xff09;二元理论&…

作者头像 李华
网站建设 2026/3/31 5:13:22

tauri2应用添加系统托盘Tray

官网文档&#xff1a;https://v2.tauri.app/learn/system-tray/ 有两种方式可以添加系统托盘&#xff0c;一种是在js中&#xff0c;一种是在rust中&#xff0c;官方都有使用案例&#xff0c;其中要注意&#xff1a; 要记得在配置文件中添加这个特性。 这里我记录一下在js中添…

作者头像 李华
网站建设 2026/3/15 2:28:39

大模型的数学工厂:揭秘GPU与TPU如何重塑AI计算架构

文章探讨了为什么大语言模型需要专门的硬件而非传统CPU。LLM本质上是数学工厂&#xff0c;执行大规模并行矩阵乘法运算。CPU因设计用于逻辑运算和分支决策而不适合处理这种计算密集型任务。GPU通过大规模并行核心和Tensor Core优化矩阵运算&#xff0c;而Google的TPU采用脉动阵…

作者头像 李华
网站建设 2026/3/25 10:21:57

智能环境测试仪设计

目录智能环境测试仪的设计要点关键技术实现应用场景示例源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;智能环境测试仪的设计要点 硬件设计 采用高精度传感器模块&#xff08;如温湿度传感器、PM2.5传感器、CO₂传感器等&#xff09;&…

作者头像 李华
网站建设 2026/3/31 10:03:01

智能货车集装箱系统

目录智能货车集装箱系统的核心功能系统的主要技术组成数据分析与优化应用安全监控与风险预警行业应用价值体现源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;智能货车集装箱系统的核心功能 智能货车集装箱系统通过物联网、大数据和人工…

作者头像 李华