`
joerong666
  • 浏览: 418039 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java线程学习笔记之Executor

 
阅读更多

并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor在执行时使用内部的线程池完成操作。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。有两种任务:RunnableCallable,Callable是需要返回值的任务。Task Submitter把任务提交给Executor执行,他们之间需要一种通讯手段,这种手段的具体实现,通常叫做FutureFuture通常包括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

包含ExecutorExecutorServiceScheduledExecutorServiceThreadFactory 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()已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionServicetake方法是,会返回按完成顺序放回任务的结果,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中的实现方式。 基本知识部分包含了Java线程编程的基础内容,它们是并发编程的基石。 任务Runnable是一个接口...

    java多线程学习笔记

    - Java线程有10个优先级,从MIN_PRIORITY(1)到MAX_PRIORITY(10),默认优先级是NORM_PRIORITY(5)。 - 优先级高的线程并不保证一定先执行,线程调度还受到操作系统的限制。 6. **线程中断** - **interrupt()...

    java多线程笔记全手打

    本笔记全面涵盖了多线程的学习,包括基础理论和实践代码,旨在帮助开发者深入理解并掌握Java多线程技术。 一、线程基础知识 线程是操作系统分配CPU时间的基本单位,一个进程中可以包含多个线程。Java通过`Thread`类...

    多线程-狂神说Java学习笔记

    本学习笔记将深入探讨Java多线程的相关知识,包括其原理、实现方式、同步机制以及常见问题。 ### 一、多线程的基本概念 多线程是指在一个程序中存在两个或更多的执行线程,这些线程共享同一内存空间,但各自拥有...

    java多线程代码笔记

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,从而提高系统效率和资源利用率。在Java中,实现多线程有两种主要方式:通过实现`Runnable`接口和继承`Thread`类。 首先,让我们从创建线程开始。当...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

     《Java JDK 7学习笔记》将IDE操作纳为教学内容之一,使读者能与实践结合,提供的视频教学能更清楚地帮助读者掌握操作步骤。 内容简介 书籍 计算机书籍  《java jdk 7学习笔记》是作者多年来教学实践经验的总结...

    多线程学习笔记.docx

    Java提供了interrupt()方法来中断线程,但需要注意的是,中断并不会立即终止线程,而是设置线程的中断标志。线程可以通过检查中断标志来决定是否结束执行,例如在while循环中检查isInterrupted()或在捕获...

    JAVA课程学习笔记.doc

    本篇学习笔记将深入解析Java线程池的框架、结构、原理以及相关源码,帮助读者全面理解线程池的工作机制。 1. 线程池模块结构 线程池框架分为多层结构,其中包括核心实现类、辅助类和接口等组件。例如,`sun.nio.ch....

    JAVA并发编程实践-线程执行-学习笔记

    占式线程调度是Java和大多数现代操作系统采用的线程调度策略。在这种模式下,操作系统决定何时以及哪个线程将获得CPU的执行时间。线程的执行不是由线程自身控制,而是由操作系统通过时间片轮转或者优先级调度等方式...

    马士兵多线程笔记.zip

    7. **线程池**:Java的Executor框架引入了线程池的概念,通过预先创建一组可重用的线程,可以有效地管理和控制线程数量,避免频繁创建和销毁线程带来的开销。 8. **并发集合**:Java的并发包(java.util.concurrent...

    线程四笔记,对于正在学习Java的朋友可以参考一下

    【线程基础与Java线程】 Java线程是并发编程的核心,它允许程序同时执行多个任务,提升系统效率。在Java中,有两种主要的创建线程的方式:实现Runnable接口和继承Thread类。 1. **实现Runnable接口**: - 创建一...

    学习笔记 java\CoreJava笔记\CoreJava_day18.doc

    第十八天的学习笔记主要涵盖了`synchronized`关键字的使用及其在处理共享数据时的重要性,同时也提到了新线程类的创建方式。 `synchronized`关键字用于实现线程同步,它可以作用于方法或代码块,以控制对特定对象的...

    java学习笔记JDK6课件和课本代码

    本资源"java学习笔记JDK6课件和课本代码"是一个针对初学者和有一定基础的开发者的学习资料集合,它涵盖了JDK6版本的相关内容。 JDK6是Java历史上的一个重要版本,发布于2006年,引入了许多新特性,包括增强的Swing...

    Java线程系列操作.zip

    在Java编程中,线程是程序执行的基本单元,它允许应用程序同时执行多个任务。...通过对这些示例代码的学习,我们可以深入理解Java线程的创建、管理、同步以及并发编程中的最佳实践,从而提升我们的多线程编程能力。

    JUC多线程学习个人笔记

    1. **线程池**:JUC通过Executor框架实现了线程池,允许开发者根据需求创建和管理线程。线程池可以减少线程创建和销毁的开销,提高系统的响应速度和资源利用率。线程池的核心组件包括ExecutorService、...

    java学习笔记.7z

    这份"java学习笔记.7z"压缩包中包含了作者在学习Java过程中整理的基础知识,旨在为初学者提供指导。以下是笔记中可能涵盖的重要知识点的详细解释: 1. **概述** - Java的历史:由Sun Microsystems开发,后被Oracle...

    java学习笔记

    Java学习笔记是一个全面涵盖Java编程语言的学习资源,旨在帮助初学者和有经验的开发者深入理解并熟练掌握Java技术。这份文档可能包含了从基础语法到高级特性的详细讲解,旨在为读者提供一个系统的Java学习路径。 在...

    java并发库学习笔记

    `ExecutorService`是`Executor`接口的一个扩展,它添加了生命周期管理的方法,包括启动、关闭和终止线程池。`ExecutorService`允许我们提交任务(Runnable或Callable)来执行,并且可以控制线程池的行为,如设置线程...

    良葛格Java JDK 5.0学习笔记.rar

    《良葛格Java JDK 5.0学习笔记》是一份详尽的教程资源,旨在帮助开发者深入理解并掌握Java开发工具包(Java Development Kit)的第5个主要版本——JDK 5.0。这份笔记涵盖了JDK 5.0中的核心特性、改进和新功能,是...

Global site tag (gtag.js) - Google Analytics