首页 > 编程语言 >Java创建线程的方式到底有哪些?

Java创建线程的方式到底有哪些?

时间:2024-03-18 16:59:20浏览次数:17  
标签:Java name Thread 哪些 created 线程 new public

一、线程的创建十种方式

一个经典的面试八股问题是:java 中有几种线程的创建方式?

一般的回答是:

  1. 实现Thread类。
  2. 实现Runable接口。
  3.  实现Callable接口。

也可以加上一个利用ExecutorService线程池: 

  1. 实现Thread类。
  2. 实现Runable接口。
  3.  实现Callable接口。
  4. 使用ExecutorService线程池

这是这个八股文的标准答案!

JDK8中又添加了个CompletableFuture来实现线程,还可以使用线程组ThreadGroup、FutureTask来实现。

  1. 实现Thread类。
  2. 实现Runable接口。
  3.  实现Callable接口。
  4. 使用ExecutorService线程池
  5. 使用CompletableFuture
  6. 基于ThreadGroup线程组
  7. 使用FutureTask

还有其他的方式吗?当然有还有 Timer、ForkJoin线程池 、Stream并行流。

  1. 实现Thread类。
  2. 实现Runable接口。
  3.  实现Callable接口。
  4. 使用ExecutorService线程池
  5. 使用CompletableFuture
  6. 基于ThreadGroup线程组
  7. 使用FutureTask
  8. 使用Timer定时类
  9. 使用ForkJoin线程池
  10. Stream并行流

具体实现代码如下:



import java.util.Arrays;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;

/**
 * @author @四维穿梭
 */
public class ThreadCreate {


    /**
     *  创建线程的方式一:继承Thread类
     */
    static class ByThread extends Thread {

        private String name;

        public ByThread(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println(name + Thread.currentThread().getName());
        }
    }

    /**
     *  创建方式二:实现Runnable接口
      */
    static class ByRunnable implements Runnable {

        private String name;

        public ByRunnable(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println(name + Thread.currentThread().getName());
        }
    }


    /**
     * 创建方式三:实现Callable接口
     */
    static  class ByCallable implements Callable<String>
    {
        private String name;

        public ByCallable(String name){
            this.name = name;
        }
        @Override
        public String call() throws Exception {
            System.out.println(name + Thread.currentThread().getName());
            return name;
        }
    }

    /**
     *  4 通过线程池创建线程
     */
    public static void createByPool(){

        /**
         * AbortPolicy:这是默认的策略。当线程池无法接受新任务时,
         *      会抛出RejectedExecutionException异常并中止任务的执行。调用者需要处理这个异常。
         * DiscardPolicy:这种策略会静默丢弃任务,不会抛出任何异常,也不会做其他处理。
         * DiscardOldestPolicy:此策略会丢弃队列中最旧的任务,以便为新被拒绝的任务腾出空间。
         *          被丢弃的任务不会被执行,而且也不会有任何通知。
         * CallerRunsPolicy:在这种策略下,如果线程池不能再接收新任务,
         *      那么提交任务的调用线程将自行执行该任务。
         */
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,3,0, TimeUnit.SECONDS
                , new LinkedBlockingDeque<Runnable>(3), Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.CallerRunsPolicy());

            poolExecutor.submit(()->{
                System.out.println("4: created by poolExecutor " + Thread.currentThread().getName());
            });
            poolExecutor.shutdown();
    }

    /**
     *  5 create by CompletableFuture
     */
    public static void createByCompletableFuture(){
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println("5: created by completableFuture " + Thread.currentThread().getName());
            return "hello CompletableFuture";
        });

        completableFuture.thenAccept(System.out::println);
    }

    /**
     *  6 created by ThreadGroup
     */
    public static void createByThreadGroup(){
        ThreadGroup threadGroup = new ThreadGroup("groupName");
        new Thread(threadGroup,()->{
            System.out.println("6: created by threadGroup " + Thread.currentThread().getName());
        }, "create by threadGroup T1").start();
        new Thread(threadGroup,()->{
            System.out.println("6: created by threadGroup " + Thread.currentThread().getName());
        }, "create by threadGroup T2").start();
    }

    /**
     *  7 create by FutureTask
     */
    public static void createByFutureTask(){
        FutureTask<String> futureTask = new FutureTask<>(()-> {
            System.out.println("7: created by futureTask " + Thread.currentThread().getName());
            return "hello FutureTask";
        });

        new Thread(futureTask).start();

    }



    /**
     *  8 created by Timer
     */
    public static void createByTimer() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("8: created by Timer " + Thread.currentThread().getName());
            }
        }, 0, 1000);
    }

    /**
     *  9 create by ForkJoinPool
     */
    public static void createByForkJoin(){
        ForkJoinPool join = new ForkJoinPool();
        join.execute(()->{
            System.out.println("9: created by ForkJoinPool " + Thread.currentThread().getName());
        });

    }

    /**
     *  10 create by parallelStream
     */
    public static void createByParallelStream(){
        List<String> list = Arrays.asList("10A", "10B");
        list.parallelStream().forEach(System.out::println);

    }


    /**
     *
     * @param args
     */


    public static void main(String[] args) {

        try {

            ByThread byThread1 = new ByThread("1: byThread ");
            byThread1.start();
            byThread1.join();

            Thread byRunnable = new Thread(new ByRunnable("2: byRunnable "));
            byRunnable.start();
            byRunnable.join();

            FutureTask<String> futureTask = new FutureTask<>(new ByCallable("3: byCallable "));
            Thread byCallable = new Thread(futureTask);
            byCallable.start();
            byCallable.join();

            createByPool();

            createByCompletableFuture();

            createByThreadGroup();

            createByFutureTask();

            createByTimer();

            createByForkJoin();

            createByParallelStream();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

1: byThread Thread-0
2: byRunnable Thread-1
3: byCallable Thread-2
4: created by poolExecutor pool-1-thread-1
5: created by completableFuture ForkJoinPool.commonPool-worker-1
hello CompletableFuture
6: created by threadGroup create by threadGroup T1
6: created by threadGroup create by threadGroup T2
7: created by futureTask Thread-3
8: created by Timer Timer-0
9: created by ForkJoinPool ForkJoinPool-1-worker-1
10B
10A
8: created by Timer Timer-0
8: created by Timer Timer-0
8: created by Timer Timer-0
8: created by Timer Timer-0

二、八股文中隐藏的问题

        “继承Thread类、实现Runnable接口、实现Callable接口”,这应该是广为人知的答案,不管是刚入行的小白,还是在业内深耕已久的老鸟,相信都背过这一道八股文。

我们看一下具体实现代码:

  /**
     *  创建线程的方式一:继承Thread类
     */
    static class ByThread extends Thread {

        private String name;

        public ByThread(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println(name + Thread.currentThread().getName());
        }
    }

    /**
     *  创建方式二:实现Runnable接口
      */
    static class ByRunnable implements Runnable {

        private String name;

        public ByRunnable(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println(name + Thread.currentThread().getName());
        }
    }


    /**
     * 创建方式三:实现Callable接口
     */
    static  class ByCallable implements Callable<String>
    {
        private String name;

        public ByCallable(String name){
            this.name = name;
        }
        @Override
        public String call() throws Exception {
            System.out.println(name + Thread.currentThread().getName());
            return name;
        }
    }


 public static void main(String[] args) {

        try {

            ByThread byThread1 = new ByThread("1: byThread ");
            byThread1.start();
       

            Thread byRunnable = new Thread(new ByRunnable("2: byRunnable "));
            byRunnable.start();
        

            FutureTask<String> futureTask = new FutureTask<>(new ByCallable("3: byCallable "));
            Thread byCallable = new Thread(futureTask);
            byCallable.start();
         
            } catch (InterruptedException e) {
            e.printStackTrace();
        }


        这三种方式,都是通过Thread.start()的启动线程。通过Runalble接口实现,先newRunnable对象,接着再new一个Thread对象,然后把Runnable丢给Thread,接着调用start()方法,此时才能真正意义上创建一条线程。通过Callable接口也类似,都是通过Thread类来实现线程创建的。

        三种方式中,只有继承Thread类,才能真正创建一条线程。因为当你用一个类,继承Thread类时,它内部所有的方法,都会被继承过来,所以当前类可以直接调用start()方法启动,更具体点来说,Java中,创建线程的方式就只有一种:调用Thread.start()方法!只有这种形式,才能在真正意义上创建一条线程!

        而例如ExecutorService线程池、ForkJoin线程池、CompletableFuture类、Timer定时器类、parallelStream并行流……,如果有去看过它们源码的小伙伴应该清楚,它们最终都依赖于Thread.start()方法创建线程。

        好了,搞清楚这点之后,再回头来看Runnable、Callable,这俩既然不是创建线程的方式,那它们究竟是什么?这点咱们放到后面去讨论,先来聊聊“Java有三种创建线程的方式”,这个以讹传讹的八股文,到底是怎么来的呢?

        究根结底,这个错误观念的源头,来自于《Java编程思想》(《Thinking In Java》)和《Java核心技术》(《Core Java》)这两本书。在《Core Java》这本书的第12、13章,专门对多线程编程进行了讲解,提到了四种创建线程的方式:

  • • ①继承Thread类,并重写run()方法;

  • • ②实现Runnable接口,并传递给Thread构造器;

  • • ③实现Callable接口,创建有返回值的线程;

  • • ④使用Executor框架创建线程池。

同样的内容,在《Thinking In Java》的第二十一章,也有重复提及到。

三、线程和线程体

        线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可以与同属一个进程的其他的线程共享进程所拥有的全部资源。
        线程体(Thread Body)则是线程运行的实际代码部分。当我们创建一个新的线程时,我们需要指定线程的入口点,也就是线程体。线程体可以是一个函数或者是一个已经被实例化的对象。线程一旦启动,操作系统就会运行线程体中的代码。
        例如,在Java中你可以通过实现 Runnable 接口来创建一个新的线程体。Runnable 接口只有一个方法:run(),在该方法内部就是线程体。

        总结起来,线程是载体,线程体是内容。

大家对Java中创建线程只有Thread.start()这一种方式有了更多的理解吧!而最开始给出的其他方式,要么是在封装Thread.start(),要么是在创建线程体,而这个所谓的线程体,更接地气的说,应该是“多线程任务”。

new Runnable(...);
new Callable(...);

        这并不是在创建线程,而是创建了两个可以提供给线程执行的“多线程任务”。

  public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

        当new``Thread对象并传入一个任务时,内部会调用init()方法,把传入的任务target传进去,同时还会给线程起个默认名字,即Thread-x,这个x会从0开始(线程名字也可以自定义)。init()方法做一系列准备工作,如安全检测、设定名称、绑定线程组、设置守护线程……,当init()方法执行完成后,就可以调用Thread.start()方法启动线程啦。

四、总结

现在,再次开头的问题是不是有了更出彩的答案呢?

Java创建线程有很多种方式啊,像实现Runnable、Callable接口、继承Thread类、创建线程池等等,不过这些方式并没有真正创建出线程,严格来说,Java就只有一种方式可以创建线程,那就是通过new Thread().start()创建。
而所谓的Runnable、Callable……对象,这仅仅只是线程体,也就是提供给线程执行的任务,并不属于真正的Java线程,它们的执行,最终还是需要依赖于new Thread()……

标签:Java,name,Thread,哪些,created,线程,new,public
From: https://blog.csdn.net/weixin_42093668/article/details/136808367

相关文章

  • java数据结构与算法刷题-----LeetCode45. 跳跃游戏 II
    java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846文章目录解题思路:时间复杂度O(n......
  • java数据结构与算法刷题-----LeetCode55. 跳跃游戏
    java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846文章目录解题思路:时间复杂度O(n......
  • 湿电子化学品常用的PFA试剂瓶规格有哪些?
    湿电子化学品是电子行业湿法制程的关键材料。湿电子化学品属于电子化学品领域的一个分支,是微电子、光电子湿法工艺制程(主要包括湿法蚀刻、清洗、显影、互联等)中使用的各种液体化工材料。超净高纯试剂是在通用试剂基础上发展起来的纯度最高的试剂,其杂质含量较优级试剂低几个数量......
  • java实现标签排序置顶
    设计思路置顶:将该数据放到全局数据收尾,其余数据顺序不变【将需要置顶数据的sort设置为全局Min(sort)-1】置尾:将数据放到全局末尾,其余不变【将需要置尾的数据sort设置为全局Max(sort)+1】交换:两位位置交换,其余不变【将需要交换数据的sort互换即可】拖动:局部顺序变化【将4插到1......
  • 【Java入门教程】第五讲:if-else控制语句
    现实世界是复杂多变的,同一个程序我们需要根据不同的场景做出不同的反应。在Java编程中,if-else 语句就是这样一种工具,它允许程序根据不同的条件执行不同的代码块。一、基础语法if-else 语句的基本语法结构如下:if(condition){//代码块1:当条件为true时执行}else......
  • Java初学第三天
    1、封装和多态封装封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作系统的源代码进行有机结合,形成类,其中数据和函数都是类的成员封装、继承、多态是面向对象的主要......
  • Java编程思想读书笔记
    1.finalize()方法垃圾回收器只能回收通过new创建的对象的内存空间,但由于Java可以调用本地方法,本地方法中有可能通过c语言的malloc()方法来分配内存,所以垃圾回收器会执行一次finalize()方法来调用C语言的free()方法(finalize()方法需要自己去编写代码去调用本地方法)来释放内存2.封......
  • Java导出多个Excel放在压缩包里
    之前做过一个导出多个Excel放在压缩包里的需求,当时也是网上找的思路,现在已经找不到之前的文章了,不多废话直接上代码。publicvoidexport(HttpServletRequestrequest,HttpServletResponseresponse){StringzipFileName="zipname.zip";List<Workbook>workbooks......
  • java springboot 指定运行端口
    javaspringboot指定运行端口 方法1:修改源代码里的“\src\main\resources\application.properties”文件,增加或修改server.port=8081 方法2:如果是已经打包好的jar包,在运行时指定端口。可以将 “\src\main\resources\application.properties” 文件复制到jar包同......
  • Spring6如此厉害的框架到底包含哪些内容
    源码下面无秘密,这是程序员的口头禅。对于强大而且设计优秀的Spring框架也是这样的,在基础代码层层堆叠之下,Spring成为了一个非常流行的框架。Spring6框架的开发者们通过层层设计和封装打造了一个功能如此之多而兼容性非常好的框架。这也是解构这个框架难点,而通过理解整个框架功能......