Java并发编程创建并运行线程的方法对比

目录
  • 一、创建并运行线程的五种方法
    • 第一种:继承Thread类
    • 第二种:实现Runnable接口
    • 第三种:实现Callable接口
    • 第四种:线程池 + execute
    • 第五种:线程池 + submit
  • 总结

一、创建并运行线程的五种方法

第一种:继承Thread类

这种方式是最基础的一种方式,学过java的朋友都知道,不做赘述。需要注意的是:覆盖实现使用的是run方法,运行线程是start方法。

public class FirstWay extends Thread  {
    @Override
    public void run() {
        System.out.println("第一种实现线程的方式:继承Thread类");
    }
    //模拟测试
    public static void main(String[] args) {
        new FirstWay().start();
    }
}

第二种:实现Runnable接口

第二种实现方式仍然很基础,继承Runnable接口,重写run方法实现线程运行逻辑。需要注意的:运行线程需要套一层new Thread 。

public class SecondWay implements Runnable{
    @Override
    public void run() {
        System.out.println("第二种实现线程的方式:实现Runnable接口");
    }
    //模拟测试
    public static void main(String[] args) {
        new Thread(new SecondWay()).start();
    }
}

第三种:实现Callable接口

第三种方式是实现Callable接口,Callable接口与Runable接口都能实现线程。

public class ThirdWay implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("第三种实现线程的方式:实现Callable接口");
        return "Callable接口带返回值,可以抛出异常";
    }

    //模拟测试
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new ThirdWay());
        new Thread(futureTask).start();
        //阻塞方法,获取call方法返回值
        System.out.println(futureTask.get());  //打印:Callable接口带返回值,可以抛出异常
    }
}

区别如下:

  • Callable接口实现线程方法是call, Runable接口实现线程方法是run
  • Callable有返回值, Runable接口不能有返回值
  • Callable接口方法call返回值可以设置泛型,如下例子中使用String数据类型
  • Callable接口方法call方法可以抛出异常,Runable接口run方法不可以抛出异常
  • Callable接口方法通过new Thread(futureTask).start()运行,FutureTask的get方法可以获取Callable接口方法call方法的返回值
  • 如果Callable接口方法call方法异常,在FutureTask的get方法调用时也会抛出同样的异常

第四种:线程池 + execute

从JDK5版本开始,java默认提供了线程池的支持,用线程池的方式运行线程可以避免线程的无限扩张导致应用宕机,同时也节省了线程频繁创建与销毁的资源与时间成本。

public class FourthWay implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() +
                ":实现线程的方式Runnable接口,但运行方式不一样,使用线程池");
    }

    public static void main(String[] args) {
        //创建一个固定大小的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        for(int i = 0;i < 10;i++){
            threadPool.execute(new FourthWay());
        }
    }
}

线程池ExecutorService使用execute方法运行Runnable接口run方法的线程实现,execute方法与run方法的共同特点是没有返回值。

pool-1-thread-5:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-2:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-4:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-4:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-4:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-1:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-4:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-3:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-2:实现线程的方式Runnable接口,但运行方式不一样,使用线程池
pool-1-thread-5:实现线程的方式Runnable接口,但运行方式不一样,使用线程池

从上面的结果中可以看出,线程池中包含五个线程。线程运行完成之后并不销毁,而是还回到线程池,下一次执行时从线程池中获取线程资源再次运行。

第五种:线程池 + submit

下面的例子线程池ExecutorService使用submit方法运行Callable接口call方法的线程实现,submit方法与call方法的共同特点是存在返回值。

  • Callable接口call方法的返回值可以由泛型定义
  • ExecutorService线程池submit方法的返回值是Future

Future的get方法可以获取call方法的返回值,同时如果call方法抛出异常,Future的get方法也会抛出异常。

public class FifthWay implements Callable<String> {
    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName() + ":Callable接口带返回值,可以抛出异常";
    }

    //模拟测试
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //保存多线程执行结果
        List<String> retList = new ArrayList<>();
        //创建一个固定大小的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        for(int i = 0;i < 10;i++){
            Future<String> future = threadPool.submit(new FifthWay());
            retList.add(future.get());
        }
        //java8 语法,打印retlist
        retList.forEach(System.out::println);
    }
}

上文代码中有一个小小的语法糖,retList.forEach(System.out::println);是java8提供的方法引用

pool-1-thread-1:Callable接口带返回值,可以抛出异常
pool-1-thread-2:Callable接口带返回值,可以抛出异常
pool-1-thread-3:Callable接口带返回值,可以抛出异常
pool-1-thread-4:Callable接口带返回值,可以抛出异常
pool-1-thread-5:Callable接口带返回值,可以抛出异常
pool-1-thread-1:Callable接口带返回值,可以抛出异常
pool-1-thread-2:Callable接口带返回值,可以抛出异常
pool-1-thread-3:Callable接口带返回值,可以抛出异常
pool-1-thread-4:Callable接口带返回值,可以抛出异常
pool-1-thread-5:Callable接口带返回值,可以抛出异常

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java创建并运行线程的方法

    Java线程类也是一个object类,它的实例都继承自java.lang.Thread或其子类. 可以用如下方式用java中创建一个线程: Tread thread = new Thread(); 执行该线程可以调用该线程的start()方法: thread.start(); 在上面的例子中,我们并没有为线程编写运行代码,因此调用该方法后线程就终止了. 编写线程运行时执行的代码有两种方式:一种是创建Thread子类的一个实例并重写run方法,第二种是创建类的时候实现Runnable接口.接下来我

  • 创建并运行一个java线程方法介绍

    要解释线程,就必须明白什么是进程. 什么是进程呢? 进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间.当用户再次点击左面的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间.目前操作系统都支持多进程. 要点:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间. 线程--概念 在明白进程后,就比较容易理解线程的概念. 什么是线程呢? 是进程中的一个实体,是被

  • Java并发编程示例(七):守护线程的创建和运行

    Java有一种特殊线程,守护线程,这种线程优先级特别低,只有在同一程序中的其他线程不执行时才会执行. 由于守护线程拥有这些特性,所以,一般用为为程序中的普通线程(也称为用户线程)提供服务.它们一般会有一个无限循环,或用于等待请求服务,或用于执行任务等.它们不可以做任何重要的工作,因为我们不确定他们什么时才能分配到CPU运行时间,而且当没有其他线程执行时,它们就会自动终止.这类线程的一个典型应用就是Java的垃圾回收. 在本节示例中,我们将创建两个线程,一个是普通线程,向队列中写入事件:另外一个是

  • java并发编程专题(二)----如何创建并运行java线程

    实现线程的两种方式 上一节我们了解了关于线程的一些基本知识,下面我们正式进入多线程的实现环节.实现线程常用的有两种方式,一种是继承Thread类,一种是实现Runnable接口.当然还有第三种方式,那就是通过线程池来生成线程,后面我们还会学习,一步一个脚印打好基础. Runnable接口: public interface Runnable { public abstract void run(); } Thread类: public class Thread implements Runnab

  • Java并发编程创建并运行线程的方法对比

    目录 一.创建并运行线程的五种方法 第一种:继承Thread类 第二种:实现Runnable接口 第三种:实现Callable接口 第四种:线程池 + execute 第五种:线程池 + submit 总结 一.创建并运行线程的五种方法 第一种:继承Thread类 这种方式是最基础的一种方式,学过java的朋友都知道,不做赘述.需要注意的是:覆盖实现使用的是run方法,运行线程是start方法. public class FirstWay extends Thread { @Override p

  • Java并发编程示例(五):线程休眠与恢复

    有时,我们需要在指定的时间点中断正在执行的线程.比如,每分钟检查一次传感器状态的线程,其余时间,线程不需要做任何事情.在此期间,线程不需要使用计算机的任何资源.过了这段时间之后,并且当Java虚拟机调度了该线程,则该线程继续执行.为此,你可以使用Thread类的sleeep()方法.该方法以休眠的方式来推迟线程的执行,而且整数类型的参数则指明休眠的毫秒数.当调用sleep()方法,休眠时间结束后,Java虚拟机分配给线程CPU运行时间,线程就会继续执行. 另一种是用sleep()方法的方式是通过

  • Java并发编程示例(一):线程的创建和执行

    开门见山 在IT圈里,每当我们谈论并发时,必定会说起在一台计算机上同时运行的一系列线程.如果这台电脑上有多个处理器或者是一个多核处理器,那么这时是实实在在的"同时运行":但是,如果计算机只有一个单核处理器,那么这时的"同时运行"只是表象而已. 所有的现代操作系统全部支持任务的并发执行.你可以边听音乐,边上网看新闻,还不耽误首发电子邮件.我们可以说,这种并发是 进程级并发 .在进程内部,我也可以看到有许许多多的并发任务.我们把运行在一个进程里面的并发任务称 线程. 和

  • Java并发编程示例(十):线程组

    对线程分组是Java并发API提供的一个有趣功能.我们可以将一组线程看成一个独立单元,并且可以随意操纵线程组中的线程对象.比如,可以控制一组线程来运行同样的任务,无需关心有多少线程还在运行,还可以使用一次中断调用中断所有线程的执行. Java提供了ThreadGroup类来控制一个线程组.一个线程组可以通过线程对象来创建,也可以由其他线程组来创建,生成一个树形结构的线程. 根据<Effective Java>的说明,不再建议使用ThreadGroup.建议使用Executor. --D瓜哥特此

  • Java并发编程示例(三):线程中断

    一个多线程的Java程序,直到所有线程执行完成,整个程序才会退出.(需要注意的是,是所有非后台线程(non-daemon thread)执行完成:如果一个线程执行了System.exit()方法,程序也会退出.)有时,你想中止一个线程的执行,例如你想退出程序,或者你想取消一个正在执行的任务等. Java提供了中断机制,可以让我们显式地中断我们想中止执行的线程.中断机制的一个特征就是我们可以检查线程是否已经被中断,进而决定是否响应中止请求.线程也可以忽略中止请求,继续执行. 在本节,我们所开发的示

  • 详解Java多线程编程中CountDownLatch阻塞线程的方法

    直译过来就是倒计数(CountDown)门闩(Latch).倒计数不用说,门闩的意思顾名思义就是阻止前进.在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程. CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作.例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工

  • java并发编程专题(一)----线程基础知识

    在任何的生产环境中我们都不可逃避并发这个问题,多线程作为并发问题的技术支持让我们不得不去了解.这一块知识就像一个大蛋糕一样等着我们去分享,抱着学习的心态,记录下自己对并发的认识. 1.线程的状态: 线程状态图: 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权. 3.运行状态(Running):就绪状态的线程获取了CPU,执行程序代码. 4

  • Java并发编程之线程创建介绍

    目录 1.线程与进程 2.线程的创建与运行 1.线程与进程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是一个实体,一个进程中至少有一个线程,是CPU调度和分配的基本单位,进程中的多个线程共享进程的资源. 进程的三个特征: 动态性 : 进程是运行中的程序,要动态的占用内存,CPU和网络等资源. 独立性 : 进程与进程之间是相互独立的,彼此有自己的独立内存区域. 并发性 : 假如CPU是单核,同一个时刻其实内存中只有一个进程在被执行.CPU会分时轮询切换依次为每

随机推荐