当前位置: 首页 > news >正文

【多线程】线程的三种常见创建方式

文章目录

    • 线程创建方式1——Thread
    • 线程创建方式2——Runnable
    • 线程创建方式2——匿名内部类
    • 线程创建方式3——Callable、FutureTask,带返回值

线程其实是程序中的一条执行路径。
那怎样的程序才是多线程程序呢? 例如12306网站就是支持多线程的,因为同时可以有很多人一起进入网站购票,而且每一个人互不影响。再比如百度网盘,可以同时下载或者上传多个文件。这些程序中其实就有多条执行路径,每一条执行执行路径就是一条线程,所以这样的程序就是多线程程序。下面会逐一介绍三种常见的线程创建方式。

线程创建方式1——Thread

Java为开发者提供了一个类叫做Thread,此类的对象用来表示线程。创建线程并执行线程的步骤如下

1.定义一个子类继承Thread类,并重写run方法
2.创建Thread的子类对象
3.调用start方法启动线程(启动线程后,会自动执行run方法中的代码)

public class MyThread extends Thread{// 2、必须重写Thread类的run方法@Overridepublic void run() {// 描述线程的执行任务。for (int i = 1; i <= 5; i++) {System.out.println("子线程MyThread输出:" + i);}}
}

再定义一个测试类,在测试类中创建MyThread线程对象,并启动线程

public class ThreadTest1 {// main方法是由一条默认的主线程负责执行。public static void main(String[] args) {// 3、创建MyThread线程类的对象代表一个线程Thread t = new MyThread();// 4、启动线程(自动执行run方法的)t.start(); for (int i = 1; i <= 5; i++) {System.out.println("主线程main输出:" + i);}}
}

打印结果如下图所示,我们会发现MyThread和main线程在相互抢夺CPU的执行权(注意:哪一个线程先执行,哪一个线程后执行,目前我们是无法控制的,每次输出结果都会不一样
在这里插入图片描述

最后我们还需要注意一点:不能直接去调用run方法,如果直接调用run方法就不认为是一条线程启动了,而是把Thread当做一个普通对象,此时run方法中的执行的代码会成为主线程的一部分。此时执行结果是这样的。

线程创建方式2——Runnable

Java为开发者提供了一个Runnable接口,该接口中只有一个run方法,意思就是通过Runnable接口的实现类对象专门来表示线程要执行的任务。具体步骤如下

1.先写一个Runnable接口的实现类,重写run方法(这里面就是线程要执行的代码)
2.再创建一个Runnable实现类的对象
3.创建一个Thread对象,把Runnable实现类的对象传递给Thread
4.调用Thread对象的start()方法启动线程(启动后会自动执行Runnable里面的run方法)

代码如下:先准备一个Runnable接口的实现类

/*** 1、定义一个任务类,实现Runnable接口*/
public class MyRunnable implements Runnable{// 2、重写runnable的run方法@Overridepublic void run() {// 线程要执行的任务。for (int i = 1; i <= 5; i++) {System.out.println("子线程输出 ===》" + i);}}
}

再写一个测试类,在测试类中创建线程对象,并执行线程

public class ThreadTest2 {public static void main(String[] args) {// 3、创建任务对象。Runnable target = new MyRunnable();// 4、把任务对象交给一个线程对象处理。//  public Thread(Runnable target)new Thread(target).start();for (int i = 1; i <= 5; i++) {System.out.println("主线程main输出 ===》" + i);}}
}

运行上面代码,结果如下图所示**(注意:没有出现下面交替执行的效果,也是正常的)**
在这里插入图片描述

线程创建方式2——匿名内部类

这种写法不是新知识,只是换一种写法。刚刚我们的第二种线程的创建方式,需要写一个Runnable接口的实现类,然后再把Runnable实现类的对象传递给Thread对象。

现在如果不想写Runnable实现类,于是可以直接创建Runnable接口的匿名内部类对象,传递给Thread对象。

代码如下

public class ThreadTest2_2 {public static void main(String[] args) {// 1、直接创建Runnable接口的匿名内部类形式(任务对象)Runnable target = new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println("子线程1输出:" + i);}}};new Thread(target).start();// 简化形式1:new Thread(new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println("子线程2输出:" + i);}}}).start();// 简化形式2:new Thread(() -> {for (int i = 1; i <= 5; i++) {System.out.println("子线程3输出:" + i);}}).start();for (int i = 1; i <= 5; i++) {System.out.println("主线程main输出:" + i);}}
}

线程创建方式3——Callable、FutureTask,带返回值

已经有两种了为什么还有要第三种呢? 这样,我们先分析一下前面两种都存在的一个问题。然后再引出第三种可以解决这个问题。

  • 假设线程执行完毕之后有一些数据需要返回,前面两种方式重写的run方法均没有返回结果。

    public void run(){...线程执行的代码...
    }
    
  • JDK5提供了Callable接口和FutureTask类来创建线程,它最大的优点就是有返回值。

    在Callable接口中有一个call方法,重写call方法就是线程要执行的代码,它是有返回值的

第三种创建线程的方式,步骤如下

1.先定义一个Callable接口的实现类,重写call方法
2.创建Callable实现类的对象
3.创建FutureTask类的对象,将Callable对象传递给FutureTask
4.创建Thread对象,将Future对象传递给Thread
5.调用Thread的start()方法启动线程(启动后会自动执行call方法)
等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中
6.调用FutrueTask对的get()方法获取返回结果

代码如下:先准备一个Callable接口的实现类

class MyCallable implements Callable<Integer >{public int target;public MyCallable(int s){target =s;}@Overridepublic Integer call() throws Exception {int sum=0;for(int i=0;i<=target;i++){sum+=i;}return sum;}
}

再定义一个测试类,在测试类中创建线程并启动线程,还要获取返回结果。


public class test_thread2 {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer>call=new MyCallable(100);FutureTask<Integer>f1=new FutureTask<>(call);new Thread(f1).start();Callable<Integer>call1=new MyCallable(200);FutureTask<Integer>f2=new FutureTask<>(call1);new Thread(f2).start();int res1 = f1.get();System.out.println(res1);int res2=f2.get();System.out.println(res2);}
}

相关文章:

  • jenkins-cicd基础操作
  • SpringBoot第56讲:SpringBoot集成文件 - 集成EasyExcel之Excel导入导出
  • php中WebSocket简单使用
  • 外包干了2个多月,技术明显有退步了。。。。。
  • Day52力扣打卡
  • 拨号连接bat命令和拨号错误623,系统无法找到此连接的电话簿项的解决方法
  • JavaWeb(二)
  • 小纸条..
  • MATLAB 系统辨识 - 在线估计 - Online Estimation
  • c语言上机小练(有点难)
  • Golang实践录:读取toml配置
  • 免费的AI智能改写工具,让你的文章每一篇都是原创
  • ES6中的Promise
  • Ubuntur编译ROS报错:error PCL requires C++14 or above
  • 流量分析1--菜刀666
  • 《深入 React 技术栈》
  • Cookie 在前端中的实践
  • CSS实用技巧干货
  • Docker入门(二) - Dockerfile
  • EOS是什么
  • es6--symbol
  • Java教程_软件开发基础
  • Js基础——数据类型之Null和Undefined
  • LeetCode29.两数相除 JavaScript
  • MySQL QA
  • nfs客户端进程变D,延伸linux的lock
  • php的插入排序,通过双层for循环
  • python大佬养成计划----difflib模块
  • spring security oauth2 password授权模式
  • windows-nginx-https-本地配置
  • Yii源码解读-服务定位器(Service Locator)
  • 关于springcloud Gateway中的限流
  • 那些被忽略的 JavaScript 数组方法细节
  • 如何胜任知名企业的商业数据分析师?
  • 十年未变!安全,谁之责?(下)
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 世界上最简单的无等待算法(getAndIncrement)
  • 网络应用优化——时延与带宽
  • 原生js练习题---第五课
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 如何在招聘中考核.NET架构师
  • 正则表达式-基础知识Review
  • ​Spring Boot 分片上传文件
  • # 数论-逆元
  • ###项目技术发展史
  • (8)STL算法之替换
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (转)shell中括号的特殊用法 linux if多条件判断
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core WebAPI中封装Swagger配置
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET NPOI导出Excel详解