Java线程池并发执行多个任务

Java在语言层面提供了多线程的支持,线程池能够避免频繁的线程创建和销毁的开销,因此很多时候在项目当中我们是使用的线程池去完成多线程的任务。
Java提供了Executors 框架提供了一些基础的组件能够轻松的完成多线程异步的操作,Executors提供了一系列的静态工厂方法能够获取不同的ExecutorService实现,ExecutorService扩展了Executors接口,Executors相当简单:

public interface Executor {
    void execute(Runnable command);
}

把任务本身和任务的执行解耦了,如果说Runnable是可异步执行任务的抽象,那Executor就是如何执行可异步执行任务的抽象,说起来比较绕口。
本文不讲解线程的一些基础知识,因为网上的其他文章已经写的足够详细和泛滥。我写写多个异步任务的并发执行与结果的获取问题。假设这样一个场景:我们要组装一个对象,这个对象由大量小的内容组成,这些内容是无关联无依赖关系的,如果我们串行的去执行,如果每个任务耗时10秒钟,一共有10个任务,那我们就需要100秒才能获取到结果。显然我们可以采用线程池,每个任务起一个线程,忽略线程启动时间,我们只需要10秒钟就能获取到结果。这里还有两种选择,这10秒钟我们可以去做其他事,也可以等待结果。
我们来完成这样的操作:

// 这是任务的抽象
class GetContentTask implements Callable<String> {
		
		private String name;
		
		private Integer sleepTimes;
		
		public GetContentTask(String name, Integer sleepTimes) {
			this.name = name;
			this.sleepTimes = sleepTimes;
		}
		public String call() throws Exception {
			// 假设这是一个比较耗时的操作
			Thread.sleep(sleepTimes * 1000);
			return "this is content : hello " + this.name;
		}
		
	}

采用completionService :

// 方法一
		ExecutorService executorService = Executors.newCachedThreadPool();
		CompletionService<String> completionService = new ExecutorCompletionService(executorService);
		ExecuteServiceDemo executeServiceDemo = new ExecuteServiceDemo();
		// 十个
		long startTime = System.currentTimeMillis();
		int count = 0;
		for (int i = 0;i < 10;i ++) {
			count ++;
			GetContentTask getContentTask = new ExecuteServiceDemo.GetContentTask("micro" + i, 10);
			completionService.submit(getContentTask);
		}
		System.out.println("提交完任务,主线程空闲了, 可以去做一些事情。");
		// 假装做了8秒种其他事情
		try {
			Thread.sleep(8000);
			System.out.println("主线程做完了,等待结果");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			// 做完事情要结果
			for (int i = 0;i < count;i ++) {
				Future<String> result = completionService.take();
				System.out.println(result.get());
			}
			long endTime = System.currentTimeMillis();
			System.out.println("耗时 : " + (endTime - startTime) / 1000);
		}  catch (Exception ex) {
			System.out.println(ex.getMessage());
		}

执行结果为:

提交完任务,主线程空闲了, 可以去做一些事情。
主线程做完了,等待结果
this is content : hello micro9
this is content : hello micro7
this is content : hello micro2
this is content : hello micro5
this is content : hello micro4
this is content : hello micro8
this is content : hello micro1
this is content : hello micro3
this is content : hello micro0
this is content : hello micro6
耗时 : 10

如果多个不想一个一个提交,可以采用 invokeAll一并提交,但是会同步等待这些任务

// 方法二
		ExecutorService executeService = Executors.newCachedThreadPool();
		List<GetContentTask> taskList = new ArrayList<GetContentTask>();
		long startTime = System.currentTimeMillis();
		for (int i = 0;i < 10;i ++) {
			taskList.add(new GetContentTask("micro" + i, 10));
		}
		try {
			System.out.println("主线程发起异步任务请求");
			List<Future<String>> resultList = executeService.invokeAll(taskList);
			// 这里会阻塞等待resultList获取到所有异步执行的结果才会执行 
			for (Future<String> future : resultList) {
				System.out.println(future.get());
			}
			// 主线程假装很忙执行8秒钟
			Thread.sleep(8);
			long endTime = System.currentTimeMillis();
			System.out.println("耗时 : " + (endTime - startTime) / 1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
主线程发起异步任务请求
this is content : hello micro0
this is content : hello micro1
this is content : hello micro2
this is content : hello micro3
this is content : hello micro4
this is content : hello micro5
this is content : hello micro6
this is content : hello micro7
this is content : hello micro8
this is content : hello micro9
耗时 : 10

如果一系列请求,我们并不需要等待每个请求,我们可以invokeAny,只要某一个请求返回即可。

ExecutorService executorService = Executors.newCachedThreadPool();
		ArrayList<GetContentTask> taskList = new ArrayList<GetContentTask>();
		taskList.add(new GetContentTask("micro1",3));
		taskList.add(new GetContentTask("micro2", 6));
		try {
			List<Future<String>> resultList = executorService.invokeAll(taskList);// 等待6秒 
//			String result2 = executorService.invokeAny(taskList); // 等待3秒
			// invokeAll 提交一堆任务并行处理并拿到结果
			// invokeAny就是提交一堆并行任务拿到一个结果即可
			for (Future<String> result : resultList) {
				System.out.println(result.get());
			}
//			System.out.println(result2);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("主线程等待");

如果我虽然发送了一堆异步的任务,但是我只等待一定的时间,在规定的时间没有返回我就不要了,例如很多时候的网络请求其他服务器如果要数据,由于网络原因不能一直等待,在规定时间内去拿,拿不到就我使用一个默认值。这样的场景,我们可以使用下面的写法:

try {
			ExecutorService executorService = Executors.newCachedThreadPool();
			List<Callable<String>> taskList = new ArrayList<Callable<String>>();
			taskList.add(new GetContentTask("micro1", 4));
			taskList.add(new GetContentTask("micro2", 6));
			// 等待五秒
			List<Future<String>> resultList = executorService.invokeAll(taskList, 5, TimeUnit.SECONDS);
			for (Future<String> future : resultList) {
				System.out.println(future.get());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
this is content : hello micro1
java.util.concurrent.CancellationException
	at java.util.concurrent.FutureTask.report(FutureTask.java:121)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.micro.demo.spring.ExecuteServiceDemo.main(ExecuteServiceDemo.java:105)

因为只等待5秒,6秒的那个任务自然获取不到,抛出异常,如果将等待时间设置成8秒,就都能获取到。

  • 12
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java中,使用线程池可以方便地开启多线程来处理任务Java提供了几种常见的线程池创建方式,其中推荐使用ThreadPoolExecutor的构造器来创建适合业务场景的线程池。\[1\] 常见的线程池创建方式包括: 1. FixedThreadPool(固定线程池):线程池的大小一旦达到固定数量就会保持不变,适用于需要控制并发线程数量的场景。 2. SingleThreadExecutor(单线程化的线程池):只有一个线程的线程池任务按照提交的次序顺序执行,适用于需要按顺序执行任务的场景。 3. CachedThreadPool(可缓存线程池):线程池的大小可以根据需要自动调整,适用于需要处理大量短期任务的场景。 使用线程池的好处是可以提前创建好多个线程,放入线程池中,使用时直接获取,使用完后放回池中,避免频繁创建和销毁线程,实现线程的重复利用。线程池能够独立负责线程的创建、维护和分配,提高了线程的执行效率和资源利用率。\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [java线程(线程池)使用总结](https://blog.csdn.net/domine/article/details/127342754)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值