福清市建设工程交易网站好像,宁波外贸公司一览表,手机端搜索引擎排名,遵义网吧一、前言
本文我们将讲解 Java 中的线程池 ( Thread Pool )#xff0c;从 Java 标准库中的线程池的不同实现开始#xff0c;到 Google 开发的 Guava 库的前世今生 注#xff1a;本章节涉及到很多前几个章节中阐述的知识点。我们希望你是按照顺序阅读下来的#xff0c;不然…一、前言
本文我们将讲解 Java 中的线程池 ( Thread Pool )从 Java 标准库中的线程池的不同实现开始到 Google 开发的 Guava 库的前世今生 注本章节涉及到很多前几个章节中阐述的知识点。我们希望你是按照顺序阅读下来的不然有些知识会一头雾水。 Java 语言的实现中把 Java 线程一一映射到操作系统级的线程而后者是操作系统的资源这意味着如果开发者毫无节制地创建线程那么线程资源就会被快速的耗尽。 在Windows 操作系统上每个线程要预留出 1m 的内存空间意味着 2G 的内存理论上做多只能创建 2048 个线程。而在 Linux 上最大线程数由常量 PTHREAD_THREADS_MAX 决定一般为 1024。 出于模拟并行性的目的Java 线程之间的上下文切换也由操作系统完成。因为线程上下文切换需要消耗时间所以一个简单的观点是产生的线程越多每个线程花在实际工作上的时间就越少。 为什么会有线程上下文切换 一台电脑运行起来后它的 CPU 是固定的05 年之前还是单核时代也就是一次只能运行一个线程虽然随着时间的推移现在的 CPU 已经有很多个核心比如 8 核 16 核之类的。但相比于一个应用程序能够创建的线程数那真的是太少了。而每个核心一次只能运行一个线程所以多个线程需要运行时就需要来回不停的在多个线程间切换这就是线程之间的上下文切换。 为了节制创建线程的数量也为了节省创建线程的开销因此提出了线程池的概念。线程池模式有助于节省多线程应用程序中的资源还可以在某些预定义的限制内包含并行性。
当我们使用线程池时我们可以以并行任务的形式编写并发代码并将其提交到线程池的实例中执行。
这个线程池实例控制了多个重用线程以执行这些任务。
这种线程池模式允许我们控制应用程序创建的线程数生命周期以及计划任务的执行并将传入的任务保留在队列中。
二、 Java 中的线程池 Executors/Executor/ExecutorService
关系
首先介绍一下他们三者之间的关系
Executor, ExecutorService 都是接口ExecutorService继承于ExecutorExecutors是工具类他提供对ThreadPoolExecutor的封装产生ExecutorService的具体实现类。
图一 图一可以看出 ExecutorService继承于Executor且 Executors 是一个帮助类几个用于创建线程池
图二 图二可以看出 Executors几个new的方法 返回的基本都是ExecutorService
一句话他们之间的关系就是 Executor, ExecutorService 都是接口ExecutorService继承于ExecutorExecutors是工具类他提供对ThreadPoolExecutor的封装会产生几种线程池供大家使用。 Executors/Executor
Executors 是一个帮助类提供了创建几种预配置线程池实例的方法。如果你不需要应用任何自定义的微调可以调用这些方法创建默认配置的线程池因为它能节省很多时间和代码。
Executor 和 ExecutorService 接口则用于与 Java 中不同线程池的实现协同工作。通常你应该将代码与线程池的实际实现分离并在整个应用程序中使用这些接口。
Executor 接口提供了一个 execute() 方法将 Runnable 实例提交到线程池中执行。
下面的代码是一个快速示例演示了如何使用 Executors API 获取包含了单个线程池和无限队列支持的 Executor 实例以便按顺序执行任务。
Executor executor Executors.newSingleThreadExecutor();
获取了Executor 示例后我们就可以使用 execute() 方法将一个只在屏幕上打印 Hello World 的任务提交到队列中执行。
executor.execute(() - System.out.println(Hello World));
上面这个示例使用了 lambda Java 8特性 提交任务JVM 会自动推断该任务为 Runnable
我们在Java Shell 演示下上面的代码
jshell import java.util.concurrent.*jshell Executor executor Executors.newSingleThreadExecutor();
executor java.util.concurrent.Executors$FinalizableDelegatedExecutorService1e127982jshell executor.execute(() - System.out.println(Hello World));jshell Hello World
jshell ExecutorService
ExecutorService 接口则包含大量用于控制任务进度和管理服务终止的方法。我们可以使用此接口来提交要执行的任务还可以使用此接口返回的 Future 实例控制任务的执行。
下面的示例中我们创建了一个 ExecutorService 的实例提交了一个任务然后使用返回的 Future 的 get() 方法等待提交的任务完成并返回值。
ExecutorService executorService Executors.newFixedThreadPool(10);
FutureString future executorService.submit(() - Hello World);
// 一些其它操作
String result future.get();
在实际使用时我们并不会立即调用 future.get() 方法可能会等待一些时间推迟调用它直到我们需要它的值用于计算等目的。
ExecutorService 中的 submit() 方法被重载为支持 Runnable 或 Callable 它们都是功能接口可以接收一个 lambdas 作为参数 从 Java 8 开始
使用 Runnable 作为参数的方法不会抛出异常也不会返回任何值 ( 返回 void )使用 Callable 作为参数的方法则可以抛出异常也可以返回值。
如果想让编译器将参数推断为 Callable 类型只需要 lambda 返回一个值即可。
ExecutorService 接口的更多使用范例和特性你可以访问前面的章节 一文秒懂 Java ExecutorService。