首页 > 编程语言 >Java使用线程实现异步运行

Java使用线程实现异步运行

时间:2024-07-05 17:29:58浏览次数:24  
标签:异步 Java Thread 耗时 任务 CompletableFuture 线程

在Java中,实现异步运行的一个常用方式是使用Thread类。下面,我将给出一个详细且完整的示例,该示例将创建一个简单的异步任务,该任务将模拟一个耗时的操作(比如,模拟网络请求或文件处理)。

1. 使用Thread类实现异步运行

假设我们有一个任务,该任务需要模拟一个耗时操作,比如从网络下载一个大文件。我们将使用Thread类来异步执行这个任务,以便主程序可以继续执行其他任务,而不需要等待下载完成。

public class AsyncTaskExample {  
  
    // 模拟耗时任务的Runnable实现  
    static class LongRunningTask implements Runnable {  
        @Override  
        public void run() {  
            // 模拟耗时操作,例如网络请求或文件处理  
            try {  
                // 使用Thread.sleep来模拟耗时操作  
                System.out.println("开始执行耗时任务...");  
                Thread.sleep(5000); // 假设这个任务是耗时5秒的  
                System.out.println("耗时任务完成!");  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt(); // 保持中断状态  
                System.out.println("任务被中断!");  
            }  
        }  
    }  
  
    public static void main(String[] args) {  
        // 创建Runnable实例  
        Runnable task = new LongRunningTask();  
  
        // 创建Thread实例,并将Runnable作为任务传递  
        Thread thread = new Thread(task);  
  
        // 启动线程  
        System.out.println("启动异步任务...");  
        long startTime = System.currentTimeMillis(); // 记录开始时间  
        thread.start(); // 启动线程,注意start()方法调用后,线程将独立执行  
  
        // 主线程继续执行,不等待异步任务完成  
        for (int i = 0; i < 5; i++) {  
            System.out.println("主线程正在执行其他任务... " + i);  
            try {  
                Thread.sleep(1000); // 模拟主线程正在执行其他任务  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
        }  
  
        long endTime = System.currentTimeMillis(); // 记录结束时间  
        System.out.println("主线程结束,耗时:" + (endTime - startTime) + "毫秒");  
  
        // 注意:这里的代码不会等待异步线程完成,如果我们需要等待异步线程完成,可以调用thread.join();  
        // 但是在这个例子中,我们不会这样做,以展示异步执行的特性  
    }  
}

代码解释:

(1)LongRunningTask:这是一个实现了Runnable接口的类,用于封装耗时的任务。在这个例子中,我们使用Thread.sleep(5000)来模拟耗时操作。

(2)main方法

  • 创建一个LongRunningTask的实例。

  • 使用这个实例作为参数创建一个Thread对象。

  • 调用thread.start()来启动线程,这将导致LongRunningTaskrun方法在新线程中异步执行。

  • 在主线程中,我们使用一个循环来模拟主线程正在执行的其他任务,并使用Thread.sleep(1000)来模拟这些任务的耗时。

  • 注意到主线程不会等待异步线程完成,它将继续执行直到循环结束。

注意事项:

  • 异步执行意味着主线程和异步线程将并行执行,互不干扰。

  • 如果需要主线程等待异步线程完成,可以调用thread.join()。但在上面的示例中,我们没有这样做以展示异步执行的特性。

  • 在处理多线程时,要特别注意线程安全和资源同步问题。上面的示例较为简单,没有涉及到这些高级概念。但在实际应用中,这些问题可能非常重要。

除了直接使用Thread类之外,Java还提供了其他几种实现异步运行的方法。以下是一些常用的方法,并给出详细的代码示例。

2. 使用ExecutorService

ExecutorServicejava.util.concurrent包中的一个接口,它提供了一种更灵活的方式来管理线程池中的线程。使用ExecutorService可以方便地控制线程的数量、执行异步任务,并获取任务执行的结果。

import java.util.concurrent.Callable;  
import java.util.concurrent.ExecutionException;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Future;  
  
public class ExecutorServiceExample {  
  
    // 模拟耗时任务的Callable实现  
    static class LongRunningTask implements Callable<String> {  
        @Override  
        public String call() throws Exception {  
            // 模拟耗时操作  
            Thread.sleep(5000);  
            return "任务完成";  
        }  
    }  
  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池  
        ExecutorService executor = Executors.newFixedThreadPool(2);  
  
        // 提交任务并获取Future对象  
        Future<String> future = executor.submit(new LongRunningTask());  
  
        // 主线程继续执行其他任务  
        System.out.println("主线程正在执行其他任务...");  
  
        try {  
            // 如果需要,可以等待异步任务完成并获取结果  
            String result = future.get(); // 这将会阻塞,直到任务完成  
            System.out.println("异步任务结果: " + result);  
        } catch (InterruptedException | ExecutionException e) {  
            e.printStackTrace();  
        }  
  
        // 关闭线程池(注意:这不会立即停止正在执行的任务)  
        executor.shutdown();  
  
        // 如果我们想立即停止所有正在执行的任务,可以使用shutdownNow(),但这通常不是推荐的做法  
        // executor.shutdownNow();  
    }  
}

3. 使用CompletableFuture

CompletableFuture是Java 8引入的一个类,它实现了FutureCompletionStage接口,提供了更丰富的异步编程能力。CompletableFuture可以显式地处理异步操作的结果,并且可以链式调用其他异步操作。

import java.util.concurrent.CompletableFuture;  
  
public class CompletableFutureExample {  
  
    // 模拟耗时任务的Runnable  
    static Runnable longRunningTask = () -> {  
        try {  
            // 模拟耗时操作  
            Thread.sleep(5000);  
            System.out.println("耗时任务完成!");  
        } catch (InterruptedException e) {  
            Thread.currentThread().interrupt();  
        }  
    };  
  
    public static void main(String[] args) {  
        // 使用runAsync方法提交一个异步任务,但不关心其结果  
        CompletableFuture.runAsync(longRunningTask);  
  
        // 如果我们想处理异步任务的结果,可以使用supplyAsync(返回结果)或thenApply等方法  
        // 例如:  
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
            // 模拟耗时操作并返回结果  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
            return "异步任务结果";  
        });  
  
        // 链式调用处理结果  
        future.thenAccept(result -> System.out.println("处理结果: " + result));  
  
        // 主线程继续执行其他任务  
        System.out.println("主线程正在执行其他任务...");  
  
        // 注意:main方法会立即结束,因为CompletableFuture的操作是异步的。  
        // 如果需要等待异步任务完成,可以调用future.join()(但注意,CompletableFuture没有join方法,这里只是示意)  
        // 或者使用future.get(),但这会阻塞当前线程直到任务完成。  
  
        // 为了演示,我们可以简单地让主线程等待一段时间  
        try {  
            Thread.sleep(6000); // 等待足够长的时间以确保异步任务完成  
        } catch (InterruptedException e) {  
            Thread.currentThread().interrupt();  
        }  
    }  
}  
  
// 注意:上面的CompletableFuture示例中,我使用了Thread.sleep来模拟等待异步任务完成,  
// 这在实际应用中通常不是最佳实践。在实际应用中,我们可能需要更复杂的逻辑来处理异步任务的结果。

请注意,CompletableFutureget()方法会阻塞当前线程直到异步任务完成,这与Future.get()的行为相同。

4. 如何在Java中实现异步运行

在Java中实现异步运行,通常指的是在不阻塞当前线程的情况下执行耗时操作或长时间运行的任务。Java提供了多种机制来实现异步编程,包括使用ExecutorServiceCompletableFutureFuture接口,以及Java 9及以后版本中引入的Flow.PublisherFlow.Subscriber(Reactive Streams API)等。以下是几种常见的实现异步运行的方法:

4.1 使用ExecutorService

ExecutorServicejava.util.concurrent包中的一个接口,它提供了一种管理线程池的方法,允许我们提交任务给线程池中的线程执行,而不需要显式地创建和管理线程。

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
  
public class AsyncExecutorService {  
  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池  
        ExecutorService executor = Executors.newFixedThreadPool(2);  
  
        // 提交任务给线程池执行  
        executor.submit(() -> {  
            // 耗时任务  
            System.out.println("异步任务开始执行...");  
            try {  
                Thread.sleep(5000); // 模拟耗时操作  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
            System.out.println("异步任务执行完成!");  
        });  
  
        // 主线程继续执行其他任务  
        System.out.println("主线程继续执行...");  
  
        // 注意:通常应该关闭ExecutorService,但这里为了简化示例没有包含关闭代码  
        // executor.shutdown();  
    }  
}

4.2 使用CompletableFuture

CompletableFuture是Java 8引入的一个类,用于编写异步代码。它实现了FutureCompletionStage接口,提供了丰富的API来处理异步编程中的结果。

import java.util.concurrent.CompletableFuture;  
  
public class AsyncCompletableFuture {  
  
    public static void main(String[] args) {  
        // 使用supplyAsync提交一个返回结果的异步任务  
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
            // 耗时任务  
            try {  
                Thread.sleep(5000); // 模拟耗时操作  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
            return "异步任务结果";  
        });  
  
        // 异步处理结果  
        future.thenAccept(result -> System.out.println("处理结果: " + result));  
  
        // 主线程继续执行其他任务  
        System.out.println("主线程继续执行...");  
  
        // 注意:通常不需要显式等待CompletableFuture完成,因为它会自动在后台执行  
        // 但如果我们需要等待结果,可以使用future.join()(注意:CompletableFuture没有join方法,这里只是示意)  
        // 或者使用future.get(),但这会阻塞当前线程  
    }  
}  
  
// 注意:CompletableFuture没有join方法,但我们可以使用future.get()来阻塞等待结果,  
// 或者使用future.thenRun(Runnable)等方法来在任务完成后执行某些操作,而不会阻塞当前线程。

4.3 使用Future

虽然Future接口本身不提供直接创建异步任务的方法,但它通常与ExecutorService一起使用来接收异步执行的结果。

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Future;  
  
public class AsyncFuture {  
  
    public static void main(String[] args) throws Exception {  
        // 创建一个ExecutorService  
        ExecutorService executor = Executors.newSingleThreadExecutor();  
  
        // 提交任务并获取Future对象  
        Future<String> future = executor.submit(() -> {  
            // 耗时任务  
            Thread.sleep(5000); // 模拟耗时操作  
            return "异步任务结果";  
        });  
  
        // 主线程继续执行其他任务  
        System.out.println("主线程继续执行...");  
  
        // 等待异步任务完成并获取结果  
        // 注意:这会阻塞当前线程直到任务完成  
        String result = future.get();  
        System.out.println("异步任务结果: " + result);  
  
        // 关闭ExecutorService  
        executor.shutdown();  
    }  
}

4.4 总结

以上是在Java中实现异步运行的几种常见方法。选择哪种方法取决于我们的具体需求,比如是否需要处理异步结果、是否需要控制线程池的大小、是否偏好使用Java 8的lambda表达式等。在实际应用中,通常建议使用ExecutorServiceCompletableFuture,因为它们提供了更灵活和强大的异步编程能力。

标签:异步,Java,Thread,耗时,任务,CompletableFuture,线程
From: https://blog.csdn.net/m0_72958694/article/details/140214181

相关文章

  • java中stream流的操作详解
    1.Java8之后引入的Stream流为我们提供了便捷的集合数据处理方式一,常用方法1.filter;过滤集合中符合条件的数据2.distinct();过滤掉集合中重复的元素,过滤的是所有元素都相同的对象3.sorted();对集合中元素进行排序,用来排序的元素类型必须是int才行4.limit(longn);返回前n个......
  • 从jvm层面搞懂java的i++
    >本博客将从java字节码的层面解剖为什么i=0;i=i++;仍然会等于0字节码解析:iconst_x:代表将常量x放到操作数栈中istore_x:其中x必须是局部变量表中的一个合法下标,然后我们会从操作数栈中弹出对应的栈尾的元素(需要是int)以之来进行设置iload_x:则是加载对应的局部变量表的x......
  • Java流程控制
    Scanner对象Java给我们提供了这样一个工具类,我们可以获取用户的输入。java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入。基本语法:Scanners=newScanner(System.in);通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一般......
  • 四种封装 ThreadPoolExecutor 的线程池的使用以及直接使用 ThreadPoolExecutor ,优缺点
    池化思想:线程池、字符串常量池、数据库连接池提高资源的利用率下面是手动创建线程和执行任务过程,可见挺麻烦的,而且线程利用率不高。手动创建线程对象执行任务执行完毕,释放线程对象线程池的优点:提高线程的利用率提高程序的响应速度便于统一管理线程对象可以控制最大并发......
  • Java [ 基础 ] 方法引用 ✨
    ✨探索Java基础✨Java基础:方法引用方法引用是Java8中引入的一种新特性,它使得代码更加简洁和易读。方法引用提供了一种可以直接引用已有方法作为Lambda表达式的替代方案。本文将深入介绍方法引用的基本概念、使用方法、具体实例及其在实际开发中的应用。什么是方法引用?方法......
  • java List集合排序方式
    javaList集合排序方式1.使用直接上代码packagecom.demo;importcn.hutool.core.date.DateTime;importlombok.AllArgsConstructor;importlombok.Data;importjava.util.*;importjava.util.stream.Collectors;publicclassDemoCollectionsSortMain{public......
  • Python多线程-线程池ThreadPoolExecutor
    1.线程池不是线程数量越多,程序的执行效率就越快。线程也是一个对象,是需要占用资源的,线程数量过多的话肯定会消耗过多的资源,同时线程间的上下文切换也是一笔不小的开销,所以有时候开辟过多的线程不但不会提高程序的执行效率,反而会适得其反使程序变慢,得不偿失。为了防止无尽的线程......
  • Java EE改名Jakarta EE,jakarta对程序的影响
    一、前言很多Java程序员在使用新版本的Spring6或者springboot3版本的时候,发现了一些叫jakarta的包。我在阅读开源工作流引擎camunda源代码的时候,也发展了大量jakarta的工程包。比如:camunda的webapps编译工程就提供了2种方式javax和jakarta*`assembly`-Javasourcesandtes......
  • 灾难恢复中的异步复制和同步复制
    本文分享自天翼云开发者社区《灾难恢复中的异步复制和同步复制》,作者:h****n1.异步复制异步复制用于在远程位置创建数据备份。它涉及从主存储系统复制数据到辅助存储系统,并在两个系统之间设置延迟。这种延迟可以从几秒钟到几分钟不等,具体取决于实现和网络条件。数据传输方面,异......
  • 基于javaweb二手闲置物品在线交易平台系统作品成品
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......