廖雪峰Java11多线程编程-3高级concurrent包-6ExecutorService
1. 线程池
Java语言内置多线程支持:
- 创建线程需要操作系统资源(线程资源,栈空间)
- 频繁创建和销毁线程需要消耗大量时间
假设我们有大量的小任务,可以让它排队执行,然后在一个线程池里有少量的线程来执行大量的任务。
使用线程池来复用线程,可以非常高效的执行大量小任务。

线程池:
- 线程池维护若干个线程,处于等待状态
- 如果有新任务,就分配一个空闲线程执行
- 如果所有线程都处于忙碌状态,新任务放入队列等待
2. ExecutorService
JDK提供了ExecutorService接口表示线程池:
ExecutorService executor = Executors.newFixedThreadPool(4); //固定大小的线程池
executor.submit(task1); //提交任务到线程池
executor.submit(task2);
executor.submit(task3)
常用的ExecutorService:
- FixedThreadPool:线程数固定
- CachedThreadPool:线程数根据任务动态调整
- SingleThreadExecutor:仅单线程执行
2.1 FixedThreadPool示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class PrintTask implements Runnable{
String name;
public PrintTask(String name){
this.name = name;
}
public void run(){
for(int i=0;i<3;i++){
System.out.println(i+" Hello,"+name+"!");
try{
Thread.sleep(1000);
}catch (InterruptedException e){}
}
}
}
public class ThreadPool {
public static void main(String[] args) throws InterruptedException{
ExecutorService executor = Executors.newFixedThreadPool(3); //指定线程池大小为3,提供了4个任务,会有1个任务等待有空闲线程后执行。
executor.submit(new PrintTask("Bob"));
executor.submit(new PrintTask("Alice"));
executor.submit(new PrintTask("Tim"));
executor.submit(new PrintTask("Robot"));
Thread.sleep(10000);
executor.shutdown(); //结束线程池
}
}

### 2.2 SingleThreadExecutor示例
```#java
//单个线程,所有的任务将串行执行
ExecutorService executor = Executors.newSingleThreadExecutor();
```

### 2.3 CachedThreadPool示例
```#java
//动态调整的线程池。由于CachedThreadPool会根据我们的任务,动态的调整线程的数量,所以这个任务提交后,线程池会立刻创建4个线程来执行它。
ExecutorService executor = Executors.newCachedThreadPool();
```

### 2.4 动态线程池指定最大线程数量
如果我们想要限制动态线程池中线程的上限,例如最多10个线程,这个时候,CachedThreadPool就不能够满足这个要求。
查看newCachedThreadPool源码,发现其实现的是ThreadPoolExecutor的构造方法,
```#java
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, //初始化线程池的大小
Integer.MAX_VALUE, //线程池的最大值
60L,
TimeUnit.SECONDS,
new SynchronousQueue());
}
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue )
{
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}
```
示例
```#java
//设置最大数量为10的动态线程池
ExecutorService executor = new ThreadPoolExecutor(0, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue());
```
3. ScheduledThreadPool
JDK还提供了ScheduledThreadPool,使一个任务可以定期反复执行。
执行模式:
- Fixed Rate:在固定的间隔,任务就会执行。例如每隔3秒任务就会启动,而不管这个任务已执行了多长时间、是否结束
- Fixed Delay:当任务执行完毕以后,等待1秒钟再继续执行。无论任务执行多久,只有在任务结束以后,等待1秒钟才会开始执行下一次的任务。

注意:ScheduledThreadPool不会自动停止,需要手动强制结束。
3.1示例
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class HelloTask implements Runnable{
String name;
public HelloTask(String name){
this.name = name;
}
public void run(){
System.out.println("Hello,"+name+" ! It is "+LocalTime.now());
try{
Thread.sleep(1000);
}catch (InterruptedException e){}
System.out.println("Goodbye, "+name+"! It is "+LocalTime.now());
}
}
public class SchedulePool {
public static void main(String[] args) throws Exception{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(new HelloTask("Bob"),2,5,TimeUnit.SECONDS); //2秒以后开始执行,每5秒就执行这个任务
executor.scheduleWithFixedDelay(new HelloTask("Alice"),2,5,TimeUnit.SECONDS); //2秒以后开始执行,执行结束等待5秒再执行
}
}

Bob的执行频率比Alice高的多,任务开始的时间差也越来越大
问题:
1.FixedRate模式下,如果任务执行时间过长,后续任务会不会并发执行?

不会
```#java
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class HelloTask implements Runnable{
String name;
public HelloTask(String name){
this.name = name;
}
public void run(){
System.out.println("Hello,"+name+" ! It is "+LocalTime.now());
try{
Thread.sleep(10000);
}catch (InterruptedException e){}
System.out.println("Goodbye, "+name+"! It is "+LocalTime.now());
}
}
public class SchedulePool {
public static void main(String[] args) throws Exception{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(new HelloTask("Bob"),2,1,TimeUnit.SECONDS);
}
}
<img src="https://img2018.cnblogs.com/blog/1418970/201906/1418970-20190613214419984-1491212580.png" width="500" />
<font color=#FF0000><strong>2.如果任务抛出了异常,后续任务是否继续执行?</strong></font>
<font color=#458B00>不会</font>
```#java
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class HelloTask implements Runnable{
String name;
int count;
public HelloTask(String name,int count){
this.name = name;
this.count = count;
}
public void run(){
System.out.println("Hello,"+name+" ! It is "+LocalTime.now()+" "+count);
try{
if(count == 3){
throw new RuntimeException("我是故意的");
}
Thread.sleep(1000);
}catch (InterruptedException e){}
System.out.println("Goodbye, "+name+"! It is "+LocalTime.now());
count++;
}
}
public class SchedulePool {
public static void main(String[] args) throws Exception{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(new HelloTask("Bob",0),2,5,TimeUnit.SECONDS);
}
}

4. java.util.Timer
jdk还提供了java.util.Timer类,这个类也可以定期执行一个任务:
- 一个Timer对应一个Thread,只能定期执行一个任务。如果要执行多个定时任务,就必须要启动多个Timer。
- 必须在主线程结束时跳用Timer.cancel()
而一个ScheduledPool就可以调度多个任务,所以完全可以用新的Scheduled取代Timer类。
5. 总结:
- JDK提供了ExecutorService实现了线程池功能
- 线程池内部维护一组线程,可以搞笑执行大量小任务
- Executors提供了静态方法创建不同类型的ExecutorService
- 必须调用shutdown()关闭ExecutorService
- ScheduledThreadPool可以定期调度多个任务
廖雪峰Java11多线程编程-3高级concurrent包-6ExecutorService的更多相关文章
- 廖雪峰Java11多线程编程-3高级concurrent包-5Atomic
Atomic java.util.concurrent.atomic提供了一组原子类型操作: 如AtomicInteger提供了 int addAndGet(int delta) int increm ...
- 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合
Concurrent 用ReentrantLock+Condition实现Blocking Queue. Blocking Queue:当一个线程调用getTask()时,该方法内部可能让给线程进入等 ...
- 廖雪峰Java11多线程编程-3高级concurrent包-1ReentrantLock
线程同步: 是因为多线程读写竞争资源需要同步 Java语言提供了synchronized/wait/notify来实现同步 编写多线程同步很困难 所以Java提供了java.util.concurre ...
- 廖雪峰Java11多线程编程-3高级concurrent包-9Fork_Join
线程池可以高效执行大量小任务: Fork/Join线程池可以执行一种特殊的任务: 把一个大任务拆成多个小任务并行执行 Fork/Join是在JDK 1.7引入的 示例:计算一个大数组的和 Fork/J ...
- 廖雪峰Java11多线程编程-3高级concurrent包-8CompletableFuture
使用Future可以获得异步执行结果 Future<String> future = executor.submit(task); String result = future.get() ...
- 廖雪峰Java11多线程编程-3高级concurrent包-7Future
JDK提供了ExecutorService接口表示线程池,可以通过submit提交一个任务. ExecutorService executor = Executors.newFixedThreadPo ...
- 廖雪峰Java11多线程编程-3高级concurrent包-3Condition
Condition实现等待和唤醒线程 java.util.locks.ReentrantLock用于替代synchronized加锁 synchronized可以使用wait和notify实现在条件不 ...
- 廖雪峰Java11多线程编程-3高级concurrent包-2ReadWriteLock
ReentrantLock保证单一线程执行 ReentrantLock保证了只有一个线程可以执行临界区代码: 临界区代码:任何时候只有1个线程可以执行的代码块. 临界区指的是一个访问共用资源(例如:共 ...
- 廖雪峰Java11多线程编程-2线程同步-3死锁
1.线程锁可以嵌套 在多线程编程中,要执行synchronized块: 必须首先获得指定对象的锁 Java的线程锁是可重入的锁.对同一个对象,同一个线程,可以多次获取他的锁,即同一把锁可以嵌套.如以下 ...
随机推荐
- 第一周课堂笔记1th
1. 计算机基本组成 CPU:中央处理器,人的大脑 内存:临时存储数据 (特点:断电即消失,容量小12G 24G) 硬盘:永久存储数据(特点:断电不消失容量大,500G 1T) ...
- JAVA API about HTTP 2
import java.io.IOException; import java.nio.charset.Charset; import java.security.KeyManagementExcep ...
- The linux command 之权限
一.修改权限 只有文件主或者超级用户才可以修改文件或者目录的权限. 符号表示法分为三种: Who the change will affect Which operation will be perf ...
- Nginx配置两份日志记录
nginx配置 版本-1.4.4 --- access_log /alidata/log/nginx/access/wordpress1.log ; access_log /alidata/log/n ...
- Java中的常量池
JVM中有: Class文件常量池.运行时常量池.全局字符串常量池.基本类型包装类对象 常量池 Class文件常量池: class文件是一组以字节为单位的二进制数据流,在java代码的编译期间,编写的 ...
- 家庭-养老院模型理解IOC和DI
控制反转 IOC 1. 概念 应用内部不负责依赖对象的创建和维护, 由第三方负责, 这样控制权就由应用内部转移到外部容器, 控制权的转移就是所谓的反转. 2. 比喻 有一户家庭(应用)有个老人(依赖对 ...
- 如何理解 if __name__ == "__main__"
小明.py 朋友眼中你是小明(__name__ == '小明'), 你自己眼中你是你自己(__name__ == '__main__'), 你编程很好, 朋友调你去帮他写程序(import 小明, 这 ...
- 阿里云ECS发送邮件失败
阿里云发送SMTP邮件失败 N多测试发现 阿里云服务器不能用25端口发邮件,配置465端口阿里云发送邮件是成功的 修改mail.rc 里的smtp 端口为465 配置如下 set from=*** ...
- [JZOJ3347] 【NOI2013模拟】树的难题
题目 题目大意 给你一棵树,每个节点有三种黑.白.灰三种颜色. 你要割掉一些边(每条边被割需要付出一定的代价),使得森林的每棵树满足: 没有黑点或至多一个白点. 思考历程 这题一看就知道是一个树形DP ...
- 表单 用jquery做输入脱离焦点 进行正则验证
<!-- 账号登录块 --> <form class="form1" action="" method="get&quo ...