【Java面试题】线程创建的三种方式及区别?

三种线程创建方式

  1. 继承Thread类,子类重写run()方法,调用子类的strat()启动线程。
  2. 实现Runnable接口,实现run()方法,调用对象start()启动线程。
  3. 实现Callable接口,实现call()方法,用FutureTask()封装实现类。使用FutureTask对象作为Thread对象调用start()启动线程,调用FutureTask对象的get()方法获取返回值()。

三种方式的优缺点 

采用继承Thread类方式:

  • 优点:编写简单。
  • 缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。

采用实现Runnable接口方式:

  • 优点:线程类只是实现了Runable接口,还可以继承其他的类。
  • 缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

Runnable和Callable的区别:

  • Callable规定的方法是call(),Runnable规定的方法是run()。
  • Callable的任务执行后可返回值,而Runnable的任务是不能返回值得。
  • Call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
  • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

总结:Runnable和Callable功能一样的,都是构造线程执行的任务;其区别可以简单理解为有无返回值的区别,通常Callable使用的比较多

继承Thread类

继承Thread类的话,必须重写run方法,在run方法中定义需要执行的任务。

class MyThread extends Thread{
      private static int num = 0;
      
     public MyThread(){
         num++;
      }
       
      @Override
      public void run() {
         System.out.println("主动创建的第"+num+"个线程");
     }
 }

创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。注意,不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

public class Test {
      public static void main(String[] args)  {
          MyThread thread = new MyThread();
          thread.start();
      }
  }
   
   
  class MyThread extends Thread{
     private static int num = 0;
      
     public MyThread(){
         num++;
     }
      
     @Override
     public void run() {
         System.out.println("主动创建的第"+num+"个线程");
     }
 }

在上面代码中,通过调用start()方法,就会创建一个新的线程了。为了分清start()方法调用和run()方法调用的区别,请看下面一个例子:

public class Test {
     public static void main(String[] args)  {
         System.out.println("主线程ID:"+Thread.currentThread().getId());
         MyThread thread1 = new MyThread("thread1");
         thread1.start();
        MyThread thread2 = new MyThread("thread2");
         thread2.run();
     }
 }
 
  
 class MyThread extends Thread{
     private String name;
      
    public MyThread(String name){
         this.name = name;
     }
     
     @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
 }

运行结果:

主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:8

从输出结果可以得出以下结论:

1)thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;

2)虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

2.实现Runnable接口 

  1. 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。 
  2. 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。 
  3. 调用线程对象的start()方法来启动该线程。
package Thread;

import java.util.concurrent.*;
//测试类
public class TestThread {
    public static void main(String[] args) throws Exception {
         testImplents();
    }

    public static void testImplents() throws Exception {
        MyThreadImplements myThreadImplements = new MyThreadImplements();
        Thread t1 = new Thread(myThreadImplements);
        Thread t2 = new Thread(myThreadImplements, "my thread -2");
        t1.start();
        t2.start();
    }
}
//线程类
class MyThreadImplements implements Runnable {
    @Override
    public void run() {
        System.out.println("通过实现Runable,线程号:" + Thread.currentThread().getName());
    }
}

3. 使用Callable接口

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

创建并启动有返回值的线程的步骤如下:

  1. 创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
  2. 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
  3. 使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

下面是一个例子:

public class Main {

   public static void main(String[] args){
 
    MyThread3 th=new MyThread3();

    //使用Lambda表达式创建Callable对象
 
      //使用FutureTask类来包装Callable对象

   FutureTask<Integer> future=new FutureTask<Integer>(
 
    (Callable<Integer>)()->{
 
       return 5;

     }

     );

   new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
 
     try{

    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
     }catch(Exception e){

    ex.printStackTrace();

   }
 
   }
 
 }

start()和run()的区别?

(具体区别可以看【Java面试题】线程中start方法和run方法的区别?

  • start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行
  • run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/80272.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

openGauss学习笔记-44 openGauss 高级数据管理-存储过程

文章目录 openGauss学习笔记-44 openGauss 高级数据管理-存储过程44.1 语法格式44.2 参数说明44.3 示例 openGauss学习笔记-44 openGauss 高级数据管理-存储过程 存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用&#xff0c;从而减少SQL语句的重复编写数量&…

QT的布局与间隔器介绍

布局与间隔器 1、概述 QT中使用绝对定位的布局方式&#xff0c;无法适用窗口的变化&#xff0c;但是&#xff0c;也可以通过尺寸策略来进行 调整&#xff0c;使得 可以适用窗口变化。 布局管理器作用最主要用来在qt设计师中进行控件的排列&#xff0c;另外&#xff0c;布局管理…

通讯协议044——全网独有的OPC HDA知识一之聚合(十二)持续坏值时间

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化&#xff0c;以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…

ide internal errors【bug】

ide internal errors【bug】 前言版权ide internal errors错误产生相关资源解决1解决2 设置虚拟内存最后 前言 2023-8-15 12:36:59 以下内容源自《【bug】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是h…

No view found for id 0x7f0901c3 for fragment解决以及线上bug排查技巧

情景再现 开发这么久&#xff0c;不知道你们是否也经历过这样的情况&#xff0c;测试或者用户&#xff0c;反馈app闪退&#xff0c;结果你自己打开开发工具&#xff0c;去调试&#xff0c;一切正常&#xff0c;然后闪退还是存在&#xff0c;只是在开发环境中不能重现。这种情况…

LeetCode——二叉树篇(五)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 404. 左叶子之和 513. 找树左下角的值 递归 迭代 112. 路径总和 113. 路径总和 II 404. 左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 /**…

C++初阶语法——new,delete开辟/销毁动态内存空间

前言&#xff1a;在C语言中&#xff0c;有malloc&#xff0c;realloc&#xff0c;calloc开辟动态内存空间&#xff0c;free销毁动态内存空间。而在C中&#xff0c;使用new开辟动态内存空间&#xff0c;delete销毁动态内存空间。不仅简化了操作&#xff0c;更为重要的是&#xf…

首起针对国内金融企业的开源组件投毒攻击事件

简述 2023年8月9日&#xff0c;墨菲监控到用户名为 snugglejack_org (邮件地址&#xff1a;SnuggleBearrxxhotmail.com&#xff09;的用户发布到 NPM 仓库中的 ws-paso-jssdk 组件包具有发向 https://ql.rustdesk[.]net 的可疑流量&#xff0c;经过确认该组件包携带远控脚本&a…

iPhone上的个人热点丢失了怎么办?如何修复iPhone上不见的个人热点?

个人热点功能可将我们的iPhone手机转变为 Wi-Fi 热点&#xff0c;有了Wi-Fi 热点后就可以与附近的其他设备共享其互联网连接。 一般情况下&#xff0c;个人热点打开就可以使用&#xff0c;但也有部分用户在升级系统或越狱后发现 iPhone 的个人热点消失了。 iPhone上的个人热点…

Go语言基础之基本数据类型

Go语言中有丰富的数据类型&#xff0c;除了基本的整型、浮点型、布尔型、字符串外&#xff0c;还有数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;等。Go 语言的基本类型和其他语言大同小异。 基本数据类型 整型 整型分为以下两个大类&#xff1a; 按…

python爬虫7:实战1

python爬虫7&#xff1a;实战1 前言 ​ python实现网络爬虫非常简单&#xff0c;只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点&#xff0c;方便以后复习。 申明 ​ 本系列所涉及的代码仅用于个人研究与讨论&#xff0c;并不会对网站产生不好…

Unity 鼠标实现对物体的移动、缩放、旋转

文章目录 1. 代码2. 测试场景 1. 代码 using UnityEngine;public class ObjectManipulation : MonoBehaviour {// 缩放比例限制public float MinScale 0.2f;public float MaxScale 3.0f;// 缩放速率private float scaleRate 1f;// 新尺寸private float newScale;// 射线pri…

RDMA qp数量和RDMA性能

QP数量上升性能下降 ​​​​​​https://icnp21.cs.ucr.edu/papers/icnp21camera-paper30.pdf 在现代云数据中心中&#xff0c;大规模分布式应用通常构建在许多机器上&#xff0c;需要使用大量并发连接进行频繁的网络通信[4]–[6]。但是&#xff0c;RDMA的性能会随着连接数的…

购买steam余额有风险吗?以及N种被红锁的情况

购买steam余额有风险吗&#xff1f;以及N种被红锁的情况 无论是打游戏的玩家&#xff0c;还是像我们这类靠倒卖装备赚钱的小商贩&#xff0c;都面临充值美金余额的问题&#xff0c;我们现在主要是找的专业充值渠道做代充。 最近我发现群里有极个别学员通过自己的方法找到了一…

菜鸟Vue教程 - 实现带国际化的注册登陆页面

初接触vue的时候觉得vue好难&#xff0c;因为项目中要用到&#xff0c;就硬着头皮上&#xff0c;慢慢的发现也不难&#xff0c;无外乎画个布局&#xff0c;然后通过样式调整界面。在通过属性和方法跟js交互。js就和我们写的java代码差不多了&#xff0c;复杂一点的就是引用这种…

“智能查单轻松实现批量快递查询,高效掌握快递物流信息!“

亲爱的用户&#xff0c;你是否常常为了查询大量快递单号而感到烦恼&#xff1f;不用担心&#xff0c;我们已经为你提供了一种高效、智能的解决方案&#xff01;现在&#xff0c;只需一键操作&#xff0c;即可实现批量快递查询&#xff0c;迅速了解每个单号的详细物流信息。 首…

代码随想录算法训练营第三十九天 | 62.不同路径,63. 不同路径 II

代码随想录算法训练营第三十九天 | 62.不同路径&#xff0c;63. 不同路径 II 62.不同路径深搜动态规划数论方法:eyes:题目总结:eyes: 63. 不同路径 II:eyes:题目总结:eyes: 62.不同路径 题目链接 视频讲解 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标…

爬虫逆向实战(八)--猿人学第十五题

一、数据接口分析 主页地址&#xff1a;猿人学第十五题 1、抓包 通过抓包可以发现数据接口是api/match/15 2、判断是否有加密参数 请求参数是否加密&#xff1f; 查看“载荷”模块可以发现有一个m加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cook…

JavaScript(JavaEE初阶系列13)

目录 前言&#xff1a; 1.初识JavaScript 2.JavaScript的书写形式 2.1行内式 2.2内嵌式 2.3外部式 2.4注释 2.5输入输出 3.语法 3.1变量的使用 3.2基本数据类型 3.3运算符 3.4条件语句 3.5循环语句 3.6数组 3.7函数 3.8对象 3.8.1 对象的创建 4.案例演示 4…

05_bitmaphyperloglogGEO

Bitmap&hyperloglog&GEO 面试问 记录对集合中的数据进行统计在移动应用中&#xff0c;需要统计每天的新增用户数和第2天的留存用户数&#xff1b;在电商网站的商品评论中&#xff0c;需要统计评论列表中的最新评论&#xff1a;在签到打卡中&#xff0c;需要统计一个月内…
最新文章