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

这篇具有很好参考价值的文章主要介绍了【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方法的区别?文章来源地址https://www.toymoban.com/news/detail-664525.html

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

到了这里,关于【Java面试题】线程创建的三种方式及区别?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React创建组件的三种方式及其区别是什么?

    在React中,创建组件的三种主要方式是函数式组件、类组件和使用React Hooks的函数式组件。以下是对每种方式的详细解释以及它们之间的区别: 1、函数式组件: 函数式组件是使用纯粹的JavaScript函数来定义的。它接收一个props对象作为参数,并返回一个用于描述组件UI的React元

    2024年02月11日
    浏览(43)
  • Java 创建多线程的三种方法

    在Java中创建多线程,往往都要通过Thread类来实现,今天学习下Java中创建多线程的三种方法[1]。 通过继承 Thread类 实现多线程。 主要方法: 1.void run(), 线程开启后,方法将被调用执行 2.void start(), 使此线程开始执行, Java虚拟机会调用run()方法 实现步骤: 1.定义类,继承 Thread类

    2024年02月05日
    浏览(57)
  • Java 多线程实现的三种方式

    Java 多线程实现方式主要有三种:继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。 1、继承 Thread 类实现多线程 继承 Thread 类的方法尽管被我列为一种多线程

    2023年04月27日
    浏览(54)
  • Java 实现多线程的三种方式

    1、三种方法的介绍和比较 1、1三种方式得介绍 1、继承Thread类 2、实现Runnable接口 3、实现Callable接口 1、2三种方法的介绍和比较 1、2、1、实现Runnable接口相比继承Thread类有如下优势 1、增强程序的健壮性,将业务逻辑与线程调度分离 2、线程池只能放入实现Runable或Callable类线程

    2024年02月02日
    浏览(47)
  • Java创建文件的三种方式

    内容来自于韩顺平学Java 在学习其视频下跟着编写 文件创建成功

    2024年04月11日
    浏览(66)
  • Java创建数组的三种方式

    这种一般用的比较多。 数组类型 [ ]  数组名称  =  new 数组类型 [ 数组长度 ] 

    2024年02月03日
    浏览(56)
  • 为Java应用创建Docker镜像的三种方式

    在 Dockerfiles 出现的很久之前,Java 开发者大多使用单体应用方式部署(WARs, JARs, EARs, 等等)。现在如你所知,最好的做法是为每个小业务单独部署的微服务方式。你构建的不是一个巨大的单体应用程序,而是使多个可以独立运行的小服务。 这正是 Docker 的用武之地。如果你想

    2023年04月26日
    浏览(52)
  • 大家都说Java有三种创建线程的方式!并发编程中的惊天骗局!

    在Java中,创建线程是一项非常重要的任务。线程是一种轻量级的子进程,可以并行执行,使得程序的执行效率得到提高。Java提供了多种方式来创建线程,但许多人都认为Java有三种创建线程的方式,它们分别是 继承Thread类、实现Runnable接口和使用线程池。 但是,你们知道吗?

    2024年02月08日
    浏览(71)
  • Springboot中使用线程池的三种方式

    前言 多线程是每个程序员的噩梦,用得好可以提升效率很爽,用得不好就是埋汰的火葬场。 这里不深入介绍,主要是讲解一些标准用法,熟读唐诗三百首,不会作诗也会吟。 这里就介绍一下springboot中的多线程的使用,使用线程连接池去异步执行业务方法。 由于代码中包含详

    2024年02月08日
    浏览(46)
  • 【面试精讲】Java线程6种状态和工作原理详解,Java创建线程的4种方式

    Java线程6种状态和工作原理详解,Java创建线程的4种方式 一、Java线程的六种状态 二、Java线程是如何工作的? 三、BLOCKED 和 WAITING 的区别 四、start() 和 run() 源码分析 五、Java创建线程的所有方式和代码详解 1. 继承Thread类 2. 实现Runnable接口 3. 实现Callable接口与FutureTask 4. 使用线

    2024年03月13日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包