并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable
,然后在提交给一个Executor
执行,Executor
在执行时使用内部的线程池完成操作。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。有两种任务:Runnable
和Callable
,Callable是需要返回值的任务。Task Submitter把任务提交给Executor执行,他们之间需要一种通讯手段,这种手段的具体实现,通常叫做Future
。Future
通常包括get ,cancel,get(timeout) 等等。Future
也用于异步变同步的场景。
伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ExecutorService executor = Executors.newSingleThreadExecutor(); Callable<Object> task = new Callable<Object>() { public Object call() throws Exception { Object result = "..."; return result; } }; Future<Object> future = executor.submit(task); // 等待到任务被执行完毕返回结果 future.get(); // 等待3秒,超时后会抛TimeoutException future.get(3, TimeUnit.SECONDS); |
Executors
包含Executor
、ExecutorService
、ScheduledExecutorService
、ThreadFactory
和 Callable
类的工厂和实用方法。支持以下各种方法:
- 创建并返回设置有常用配置字符串的 ExecutorService 的方法。
- 创建并返回设置有常用配置字符串的 ScheduledExecutorService 的方法。
- 创建并返回“包装的”ExecutorService 方法,它通过使特定于实现的方法不可访问来禁用重新配置。
- 创建并返回 ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。
- 创建并返回非闭包形式的 Callable 的方法,这样可将其用于需要 Callable 的执行方法中。
具体的方法说明如下:
-
callable(PrivilegedAction action)
返回 Callable 对象,调用它时可运行给定特权的操作并返回其结果。 -
callable(PrivilegedExceptionAction action)
返回 Callable 对象,调用它时可运行给定特权的异常操作并返回其结果。 -
callable(Runnable task)
返回 Callable 对象,调用它时可运行给定的任务并返回 null。 -
callable(Runnable task, T result)
返回 Callable 对象,调用它时可运行给定的任务并返回给定的结果。 -
defaultThreadFactory()
返回用于创建新线程的默认线程工厂。 -
newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 -
newCachedThreadPool(ThreadFactory threadFactory)
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程 -
newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 -
newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程 -
newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 -
newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 -
newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 -
newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程,并在需要时使用提供的 ThreadFactory 创建新线程。 -
newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 -
newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 -
privilegedCallable(Callable callable)
返回 Callable 对象,调用它时可在当前的访问控制上下文中执行给定的 callable 对象。 -
privilegedCallableUsingCurrentClassLoader(Callable callable)
返回 Callable 对象,调用它时可在当前的访问控制上下文中,使用当前上下文类加载器作为上下文类加载器来执行给定的 callable 对象。 -
privilegedThreadFactory()
返回用于创建新线程的线程工厂,这些新线程与当前线程具有相同的权限。 -
unconfigurableExecutorService(ExecutorService executor)
返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。 -
unconfigurableScheduledExecutorService(ScheduledExecutorService executor)
返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。
ScheduledExecutorServices
尽管ExecutorService
接口非常有用,但某些任务仍需要以计划方式执行,比如以确定的时间间隔或在特定时间执行给定的任务。这就是 ScheduledExecutorService
的应用范围,它扩展了ExecutorService
。
例如创建一个每隔 5 秒跳一次的 “心跳” 命令,使用ScheduledExecutorService
可以轻松实现:
1 2 3 4 5 6 7 8 9 10 |
public static void main(String[] args) { ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); Runnable pinger = new Runnable() { public void run() { System.out.println("PING!"); } }; ses.scheduleAtFixedRate(pinger, 5, 5, TimeUnit.SECONDS); } |
不用过于担心线程,不用过于担心用户希望取消心跳时会发生什么,也不用明确地将线程标记为前台或后台;只需将所有的计划细节留给ScheduledExecutorService
。如果用户希望取消心跳,scheduleAtFixedRate
调用将返回一个ScheduledFuture
实例,它不仅封装了结果(如果有),还拥有一个cancel
方法来关闭计划的操作。
下面是一个完整的示例,并行计算数组的和。
利用CompletionService
,生产者submit()
执行的任务。使用者take()
已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionService
的take
方法是,会返回按完成顺序放回任务的结果,CompletionService
内部维护了一个阻塞队列BlockingQueue
,如果没有任务完成,take()
方法也会阻塞。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ConcurrentCalculator { private ExecutorService exec; private CompletionService<Long> completionService; private int cpuCoreNumber; class SumCalculator implements Callable<Long> { private int[] numbers; private int start; private int end; public SumCalculator(final int[] numbers, int start, int end) { this.numbers = numbers; this.start = start; this.end = end; } public Long call() throws Exception { Long sum = 0l; for (int i = start; i < end; i++) { sum += numbers[i]; } return sum; } } public ConcurrentCalculator() { cpuCoreNumber = Runtime.getRuntime().availableProcessors(); exec = Executors.newFixedThreadPool(cpuCoreNumber); completionService = new ExecutorCompletionService<Long>(exec); } public Long sum(final int[] numbers) { for (int i = 0; i < cpuCoreNumber; i++) { int increment = numbers.length / cpuCoreNumber + 1; int start = increment * i; int end = increment * i + increment; if (end > numbers.length) end = numbers.length; SumCalculator subCalc = new SumCalculator(numbers, start, end); if (!exec.isShutdown()) { completionService.submit(subCalc); } } return getResult(); } public Long getResult() { Long result = 0l; for (int i = 0; i < cpuCoreNumber; i++) { try { Long subSum = completionService.take().get(); result += subSum; } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } return result; } public void close() { exec.shutdown(); } public static void main(String[] args) { int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 34 }; ConcurrentCalculator calc = new ConcurrentCalculator(); Long sum = calc.sum(numbers); System.out.println(sum); calc.close(); } } |
相关推荐
Java线程学习笔记涉及了Java多线程编程的多个关键知识点,本篇知识点整理将详细解释每个概念及其在Java中的实现方式。 基本知识部分包含了Java线程编程的基础内容,它们是并发编程的基石。 任务Runnable是一个接口...
- Java线程有10个优先级,从MIN_PRIORITY(1)到MAX_PRIORITY(10),默认优先级是NORM_PRIORITY(5)。 - 优先级高的线程并不保证一定先执行,线程调度还受到操作系统的限制。 6. **线程中断** - **interrupt()...
本笔记全面涵盖了多线程的学习,包括基础理论和实践代码,旨在帮助开发者深入理解并掌握Java多线程技术。 一、线程基础知识 线程是操作系统分配CPU时间的基本单位,一个进程中可以包含多个线程。Java通过`Thread`类...
本学习笔记将深入探讨Java多线程的相关知识,包括其原理、实现方式、同步机制以及常见问题。 ### 一、多线程的基本概念 多线程是指在一个程序中存在两个或更多的执行线程,这些线程共享同一内存空间,但各自拥有...
Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,从而提高系统效率和资源利用率。在Java中,实现多线程有两种主要方式:通过实现`Runnable`接口和继承`Thread`类。 首先,让我们从创建线程开始。当...
《Java JDK 7学习笔记》将IDE操作纳为教学内容之一,使读者能与实践结合,提供的视频教学能更清楚地帮助读者掌握操作步骤。 内容简介 书籍 计算机书籍 《java jdk 7学习笔记》是作者多年来教学实践经验的总结...
Java提供了interrupt()方法来中断线程,但需要注意的是,中断并不会立即终止线程,而是设置线程的中断标志。线程可以通过检查中断标志来决定是否结束执行,例如在while循环中检查isInterrupted()或在捕获...
本篇学习笔记将深入解析Java线程池的框架、结构、原理以及相关源码,帮助读者全面理解线程池的工作机制。 1. 线程池模块结构 线程池框架分为多层结构,其中包括核心实现类、辅助类和接口等组件。例如,`sun.nio.ch....
占式线程调度是Java和大多数现代操作系统采用的线程调度策略。在这种模式下,操作系统决定何时以及哪个线程将获得CPU的执行时间。线程的执行不是由线程自身控制,而是由操作系统通过时间片轮转或者优先级调度等方式...
7. **线程池**:Java的Executor框架引入了线程池的概念,通过预先创建一组可重用的线程,可以有效地管理和控制线程数量,避免频繁创建和销毁线程带来的开销。 8. **并发集合**:Java的并发包(java.util.concurrent...
【线程基础与Java线程】 Java线程是并发编程的核心,它允许程序同时执行多个任务,提升系统效率。在Java中,有两种主要的创建线程的方式:实现Runnable接口和继承Thread类。 1. **实现Runnable接口**: - 创建一...
第十八天的学习笔记主要涵盖了`synchronized`关键字的使用及其在处理共享数据时的重要性,同时也提到了新线程类的创建方式。 `synchronized`关键字用于实现线程同步,它可以作用于方法或代码块,以控制对特定对象的...
本资源"java学习笔记JDK6课件和课本代码"是一个针对初学者和有一定基础的开发者的学习资料集合,它涵盖了JDK6版本的相关内容。 JDK6是Java历史上的一个重要版本,发布于2006年,引入了许多新特性,包括增强的Swing...
在Java编程中,线程是程序执行的基本单元,它允许应用程序同时执行多个任务。...通过对这些示例代码的学习,我们可以深入理解Java线程的创建、管理、同步以及并发编程中的最佳实践,从而提升我们的多线程编程能力。
1. **线程池**:JUC通过Executor框架实现了线程池,允许开发者根据需求创建和管理线程。线程池可以减少线程创建和销毁的开销,提高系统的响应速度和资源利用率。线程池的核心组件包括ExecutorService、...
这份"java学习笔记.7z"压缩包中包含了作者在学习Java过程中整理的基础知识,旨在为初学者提供指导。以下是笔记中可能涵盖的重要知识点的详细解释: 1. **概述** - Java的历史:由Sun Microsystems开发,后被Oracle...
Java学习笔记是一个全面涵盖Java编程语言的学习资源,旨在帮助初学者和有经验的开发者深入理解并熟练掌握Java技术。这份文档可能包含了从基础语法到高级特性的详细讲解,旨在为读者提供一个系统的Java学习路径。 在...
`ExecutorService`是`Executor`接口的一个扩展,它添加了生命周期管理的方法,包括启动、关闭和终止线程池。`ExecutorService`允许我们提交任务(Runnable或Callable)来执行,并且可以控制线程池的行为,如设置线程...
《良葛格Java JDK 5.0学习笔记》是一份详尽的教程资源,旨在帮助开发者深入理解并掌握Java开发工具包(Java Development Kit)的第5个主要版本——JDK 5.0。这份笔记涵盖了JDK 5.0中的核心特性、改进和新功能,是...