使用多线程执行任务,并获取返回结果,附异步实现

news2024/11/25 12:25:33

1 获取又返回结果的 需要用到 callable接口

public class TestTask implements Callable<Student> {
    @Override
    public Student call() throws Exception {
        Thread.sleep(1500);
        Student student = new Student();
        student.setAge(10);
        student.setName("里里");
        System.out.println("线程执行"+student.toString());
        return student;
    }
}
@Data
public class Student {
    private String name;
    private Integer age;
}

2 创建一个线程池,可以用自定义线程池,也可以直接用那四种线程池

这里创建了一个包含三个线程的固定线程池

 ExecutorService executorService = Executors.newFixedThreadPool(3);
 Future<Student> future = executorService.submit(new TestTask());
 try {
            if (!future.isDone()) {
                System.out.println("Task is processing now, please wait...");
            }
            Student result = future.get();
            System.out.println("Result is: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }

线程池的介绍
根据主机情况实现自定义线程池:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class CustomThreadPool {
    // 线程池大小
    private final int poolSize;
    // 任务队列
    private final BlockingQueue<Runnable> taskQueue;
    // 线程数组
    private final Thread[] threads;

    /**
     * 构造函数
     * @param poolSize 线程池大小
     */
    public CustomThreadPool(int poolSize) {
        this.poolSize = poolSize;
        this.taskQueue = new LinkedBlockingQueue<>();
        this.threads = new Thread[poolSize];

        for (int i = 0; i < poolSize; i++) {
            threads[i] = new WorkerThread();
            threads[i].start();
        }
    }

    /**
     * 提交任务
     * @param task 任务
     */
    public void execute(Runnable task) {
        synchronized (taskQueue) {
            taskQueue.offer(task);
            taskQueue.notify();
        }
    }

    /**
     * 关闭线程池
     */
    public void shutdown() {
        for (int i = 0; i < poolSize; i++) {
            threads[i].interrupt();
        }
    }

    /**
     * 工作线程
     */
    private class WorkerThread extends Thread {
        public void run() {
            Runnable task;

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    task = taskQueue.poll(500, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    break;
                }

                if (task != null) {
                    task.run();
                }
            }
        }
    }
}

也可以通过继承 ThreadPoolExecutor 类来实现一个自定义线程池工具类。ThreadPoolExecutor 是 Java 标准库中提供的一个线程池实现,通过继承它,我们可以实现自定义的线程池。

下面是一个继承 ThreadPoolExecutor 的自定义线程池工具类的实现:

import java.util.concurrent.*;

public class CustomThreadPool extends ThreadPoolExecutor {
    public CustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public static CustomThreadPool newThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int queueSize) {
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(queueSize);
        return new CustomThreadPool(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        // 在执行任务前的操作
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        // 在执行任务后的操作
    }

    @Override
    protected void terminated() {
        super.terminated();
        // 线程池关闭时的操作
    }
}

在构造函数中,我们调用了父类 ThreadPoolExecutor 的构造函数,用于初始化线程池的一些参数,包括线程池的大小、任务队列、线程池的最大大小、线程存活时间等等。

我们还实现了一个静态方法 newThreadPool(),用于创建一个新的线程池。这个方法接收线程池的核心大小、最大大小、线程存活时间、时间单位和任务队列的大小等参数,然后创建一个 ArrayBlockingQueue 作为任务队列,然后通过父类的构造函数创建一个新的线程池。

在这个自定义线程池工具类中,我们还重写了三个方法:

  1. beforeExecute():在线程执行任务前执行的方法,可以在这里进行一些初始化操作。
  2. afterExecute():在线程执行任务后执行的方法,可以在这里进行一些清理操作。
  3. terminated():在线程池关闭时执行的方法,可以在这里进行一些资源释放操作。

这些方法可以根据需要进行重写。

使用这个自定义线程池工具类时,可以通过调用 execute() 方法来提交任务,如下所示:

CustomThreadPool pool = CustomThreadPool.newThreadPool(5, 10, 60L, TimeUnit.SECONDS, 100);

for (int i = 0; i < 10; i++) {
    final int taskNum = i;
    pool.execute(() -> {
        System.out.println("Task " + taskNum + " is running.");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskNum + " is completed.");
    });
}

pool.shutdown();

这里我们创建了一个新的线程池,并提交了一些任务。在任务执行时,我们通过重写 beforeExecute() 和 afterExecute() 方法,可以在任务执行前后进行一些操作。在线程池关闭时,我们可以通过重写 terminated() 方法来进行一些资源释放操作

在Java中,执行线程任务通常有两种方式:使用 execute 方法或使用 submit 方法。它们的区别在于:

返回值不同:execute 方法没有返回值,而 submit 方法返回一个 Future 对象。
异常处理不同:如果在执行任务时抛出异常,execute 方法会打印出异常堆栈信息,并且不会捕获异常,而 submit 方法会将异常封装到返回的 Future 对象中,可以通过调用 Future.get() 方法获取异常信息。
任务提交方式不同:execute 方法是将任务直接提交给线程池,而 submit 方法则是将任务封装成一个 Callable 对象,并将其提交给线程池。
因此,如果我们需要对线程执行的结果进行控制并捕获异常,可以使用 submit 方法,如果我们只是需要简单地提交一个任务给线程池,可以使用 execute 方法。

3 使用CompletionService

CompletionService是Java提供的一种辅助类,它可以用于管理多个Future对象并发执行的结果。CompletionService可以帮助我们避免手动迭代多个Future来获取结果的麻烦,并且可以让我们更方便地处理多个任务的执行结果。

CompletionService最常用的方法是submit(Callable<T> task)take()。通过submit方法,我们可以提交一个任务到该CompletionService中,并返回一个与该任务关联的Future对象。这个Future对象可以用来获取任务的结果。但是,与直接使用ExecutorService不同,CompletionService会将结果保存到一个阻塞队列中,使用take()方法可以从阻塞队列中取得第一个完成的任务的结果。

举个例子来说,如果有一个任务列表,我们需要依次异步执行,或者说需要一次性获取所有任务的执行结果,这种情况下可以使用CompletionService。在这个场景下,我们可以将所有任务提交到CompletionService中,然后使用take()方法取出队列中的任务结果。这样可以在处理每个任务的同时,按照完成时间顺序获取任务的结果。

以下是一个使用CompletionService实现同时处理多个任务的代码示例:

import java.util.concurrent.*;

public class CompletionServiceDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newCachedThreadPool();
        CompletionService<Integer> completionService = new ExecutorCompletionService<>(executor);
        for (int i = 0;i < 10; i++) {
            final int finalI = i;
            completionService.submit(() -> finalI);
        }

        for (int i = 0;i < 10; i++) {
            int result = completionService.take().get();
            System.out.println("Finished task " + result);
        }

        executor.shutdown();
    }
}

上述代码中,我们先定义ExecutorService(线程池),然后将其传入ExecutorCompletionService。接着,我们通过submit()方法将任务放入CompletionService中。最后,在主线程中通过循环调用take()方法来获取结果,因为结果会按照完成时间顺序被加入队列,所以可以方便的保证结果的顺序性。

结合我们的案例:

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

       // Future<Student> future = executorService.submit(new TestTask());

        CompletionService completionService = new ExecutorCompletionService(executorService);

        for (int i = 0; i <5 ; i++) {
            completionService.submit(new TestTask());
        }

        for (int i = 0; i < 5; i++) {
            System.out.println(completionService.take().get());
        }

        /*try {
            if (!future.isDone()) {
                System.out.println("Task is processing now, please wait...");
            }
            Student result = future.get();
            System.out.println("Result is: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }*/
    }
}

4 异步

异步和多线程一样,都是为了让程序运行效率更高

多线程指的是在一个程序中同时运行多条线程,这些线程相互独立,可以执行不同的任务。通过多线程,我们可以实现并发处理,提高程序的执行效率。

异步则是一种编程模型,指的是一个任务的执行不需要等待其他任务的结束,而是可以继续执行其他任务或者返回结果。异步通常使用回调函数或者事件驱动来实现。

在实际应用中,多线程和异步经常一起使用来处理并发问题。例如,在一个网络请求中,我们可以使用异步方式向服务器发起请求,同时使用多线程方式处理服务器响应的数据。这样可以提高请求的处理速度,同时也不会阻塞主线程的执行。

实操:

实现方式一 使用@Async注解

在Spring Boot应用中,我们可以使用Spring提供的@Async注解来指定一个方法为异步方法。使用该注解后,Spring会在调用该方法时在另外一个线程中执行方法,并立即将控制权返回给调用方
需要注意的是,在使用@Async注解时,我们需要在应用的配置类上加上@EnableAsync注解,开启Spring的异步机制。示例代码如下:

@Configuration
@EnableAsync
public class AppConfig {
 
    // 配置异步线程池等等相关配置...
 
}

这里我试过了,在异步方法的bean 或者是在启动类上加这个注解都可以实现异步功能,但是如果不加Enable注解的话,只有@Async注解是不能实现异步功能的。
在这里插入图片描述
在这里插入图片描述

这两处地方都可以。
另外,异步是真异步,怎么讲呢

@RequestMapping("/hello2")
    public String hello2() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(2000);

        String result = testRetryService.hh();
        long end = System.currentTimeMillis();
        System.out.println("执行耗时"+((end-start)/1000));
        return result;
    }
 @Async
    public String hh() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("异步方法输出");
        return "Hello";
    }

即使我再结果中返回异步执行的结果,他并不会等待异步任务完成再返回,也就是上述代码,在调用接口两秒后就返回(返回为空,因为并没有获取到异步的执行结果异步执行耗时长),并不是七秒。

实现方式二
使用CompletableFuture
除了使用@Async注解以外,Spring Boot还支持Java 8中的CompletableFuture来实现异步操作。CompletableFuture是一种非常强大、灵活的异步编程工具,可以方便地实现链式异步处理。

示例代码如下:

public class AsyncService {
 
    public CompletableFuture<String> doTask1() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            // 异步执行的任务1
            return "Result1";
        });
        return future;
    }
    
    public CompletableFuture<String> doTask2() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            // 异步执行的任务2
            return "Result2";
        });
        return future;
    }
}

在上述代码中,我们首先使用CompletableFuture的supplyAsync方法将一个Lambda函数包装为异步执行的任务,并返回一个CompletableFuture对象。接着,我们可以在需要异步执行的地方使用该方法,结合其他的CompletableFuture方法(如thenCompose()、thenApply()等)进行链式处理。

需要注意的是,使用CompletableFuture时,你需要手动指定线程池来执行异步任务。示例代码如下:

public class AsyncService {
    private static final Executor executor = Executors.newFixedThreadPool(5); // 线程池
    
    public CompletableFuture<String> doTask1() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            // 异步执行的任务1
            return "Result1";
        }, executor);
        return future;
    }
    
    public CompletableFuture<String> doTask2() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            // 异步执行的任务2
            return "Result2";
        }, executor);
        return future;
    }
}

附一篇文章,关于实现异步的几种方式:
面试官:实现异步的8种方式,你知道几个?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ceph对象存储使用

文章目录 对象存储简介RadosGW简介RadosGW配置RGW使用的存储池配置rgw使用的http端口配置rgw使用https配置rgw高可用 客户端s3cmd测试数据读写创建rgw用户安装s3cmd客户端配置s3cmd访问rgw测试数据读写bucket授权 对象存储简介 对象存储是无层次结构的数据存储方法&#xff0c…

QT+OpenGL反射与折射

文章目录 QTOpenGL反射与折射反射折射 QTOpenGL反射与折射 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 反射 反射这个属性表现为物体(或者物体的一部分)反射它周围的环境&#xff0c;即根据观察者…

【Python入门篇】——Python基础语法(字符串格式化,表达式格式化和数据输入)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

STM32CubeMx+HAL库+小熊派+FreeRTOS+EasyLogger+Gitee+手把手教你

文章目录 1、创建工程配置RCC与SYS配置LED配置KEY配置串口生成工程 2、手动移植Freertos获取源码移植include移植portable移植src复制并修改FreeRTOSConfig.hkeil中添加路径与配置 3、移植EasyLogger获取源码复制easylogger文件keil中添加路径修改elog.h修改elog_port.c修改elo…

MacOS自定义安装 Python

Python 下载地址 官网下载太慢&#xff0c;如下是国内的镜像源&#xff0c;各版本都有&#xff1a;Python 国内镜像 下载好后缀是 tgz 的包进行解压 tar -zxvf Python-3.9.16.tgz进入目录并且对进行配置&#xff0c;编译(根据自己的目录进行调整) ./configure --with-opens…

Excel技能之图表,会Excel就能碾压程序员

数据可视化&#xff0c;让数据说话&#xff0c;需要Excel图表的呈现。你越用心&#xff0c;画的图越好看。 同一份数据&#xff0c;用不同的Excel图表展现出来&#xff0c;效果各有千秋。使用正确的图表&#xff0c;从一堆杂乱无章的数据中找出规律。不仅要知道怎么用图表&…

sentinel 随笔 1-流控

0. 想要个半个月的旅游 最近发现算法比较有意思一些&#xff0c;什么企业框架都是看不完的… 书接 FlowSlot 1. FlowRuleChecker.checkFlow() : 配置的规则校验类 sentinel 并没有对这个Checker进行抽象的设计&#xff0c;第一次看有些别扭… package com.alibaba.csp.sent…

矢量绘图UI设计Sketch

Sketch是一款Mac操作系统上常用的矢量图形编辑软件&#xff0c;旨在帮助用户设计和创建高质量的UI和UX界面。 软件安装&#xff1a;Sketch 中文 以下是Sketch软件的一些主要特点&#xff1a; 矢量工具和对象&#xff1a;Sketch提供了多种画线、填充、阴影、文本和形状等矢量工…

Illustrator如何使用图形对象的特殊效果之实例演示?

文章目录 0.引言1.制作毛球小怪物2.制作三维立体图形3.3D剪影球体艺术海报 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对Illustrator进行了学习&#xff0c;本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本…

Unity大面积草地渲染——1、Shader控制一棵草的渲染

大家好&#xff0c;我是阿赵。 这里开始讲大面积草地渲染的第一个部分&#xff0c;一棵草的渲染。按照惯例&#xff0c;完整shader在最后。前面是原理的介绍。 一、准备的资源 这里我自己随便做了一个草的模型&#xff0c;主要是用几个面片搭建的一个简单模型。 然后我准备…

DAP之FLM算法研究

本人所写的博客都为开发之中遇到问题记录的随笔,主要是给自己积累些问题。免日后无印象,如有不当之处敬请指正(欢迎进扣群 24849632 探讨问题); 写在专栏前面https://blog.csdn.net/Junping1982/article/details/129955766 玩过自制DAP工具的一定都知道通过MDK目录的FLM文…

priority_queue

priority_queue&#xff1a;优先队列 头文件还是 < queue> 本质就是堆&#xff1a;完全二叉树 条件&#xff08;任意节点都比其孩子大&#xff08;大根堆&#xff09;&#xff09; priority_queue的默认比较是less&#xff0c;但是建出来的是大根堆&#xff1b;sort排序…

顺序表的基本操作(初始化,增,删,查,改等等)

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串...线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

智能算法系列之蚁群算法

本博客封面由ChatGPT DALLE 2共同创作而成。 文章目录 前言1. 算法思想2. 算法流程3. 细节梳理4. 算法实现4.1 问题场景4.2 代码实现 代码仓库&#xff1a;IALib[GitHub] 前言 本篇是智能算法(Python复现)专栏的第五篇文章&#xff0c;主要介绍蚁群算法(Ant Colony Optimizati…

cmd@快捷键方式@静默执行命令

文章目录 ref快捷方式执行命令行或打开文件eg:直接打开某个文件 创建快捷方式eg:快捷方式运行命令 ref How can I execute a Windows command line in background? - Super Userstbrenner/SilentCMD: SilentCMD executes a batch file without opening the command prompt wi…

如何用100天时间,让CSDN的粉丝数从0狂飙到10000

2022年10月7日&#xff0c;正式开通了CSDN账号。但因为工作忙的原因&#xff0c;一直没有时间写博客文章&#xff0c;也没有投入精力在CSDN上。理所当然的&#xff0c;我的粉丝数量很稳定&#xff0c;一直保持着0的记录。 2023年春节假期过后&#xff0c;有点空闲时间了&#x…

Tre靶场通关过程(linpeas使用+启动项编辑器提权)

Tre靶场通关 通过信息收集获得到了普通用户账号密码&#xff0c;利用PEASS-ng的linpeas脚本进行提权的信息收集&#xff0c;根据已有信息进行提权。 靶机下载地址&#xff1a; https://download.vulnhub.com/tre/Tre.zip 信息收集 靶机IP探测&#xff1a;192.168.0.129 a…

java多线程下

ThreadLocal ThreadLocal 有什么用&#xff1f;通常情况下&#xff0c;我们创建的变量是可以被任何一个线程访问并修改的。如果想实现每一个线程都有自己的专属本地变量该如何解决呢&#xff1f;JDK 中自带的ThreadLocal类正是为了解决这样的问题。 ThreadLocal类主要解决的就…

解释表情包This code will never do anything!

目录 解释一下下面这段代码 #include int main(){ while (1) ;} void unreachable(){ std::cout <<"Hello world!"<;}<> 解释一下下面这段代码 $ clang loop.cpp -01 -Wall -o loop $ ./loop Hello world! 解释一下下面这段代码 #include <iostre…

python面试题

文章目录 赋值、深拷贝和浅拷贝有什么区别&#xff1f;元组和列表有什么不同&#xff1f;和is有什么不同&#xff1f;集合怎么转字典&#xff1f;字典怎么遍历&#xff1f;如何在Python中实现多线程&#xff1f;如何实现tuple和list的转换&#xff1f;实现删除一个list里面的重…