[TOC]
# 1. 线程池应用场景
* 在程序中需要创建大量的生命周期很短的线程时,可以考虑使用线程池;
* 减少并发程序的数目,如果创建大量的并发线程会大大降低性能,甚至导致虚拟机崩溃。所以需要一个容量固定的线程池来约束线程的总数。
>[info]将Runable对象交给线程池,就会有一个线程调用`run`方法。当`run`方法退出的时候,线程不会立即死亡,而是在池中等待,准备为下一个请求提供服务。
# 2. 线程池API
**1. 创建线程池的方法**
* java.util.concurrent.Executors 5.0
* `public static ExecutorService newCachedThreadPool()`
返回一个带缓存的线程池,线程空闲60s后则终止线程。对于每个任务,如果有空闲线程可用,立即让他执行任务;如果没有,则创建一个新的线程。
* `public static ExecutorService newFixedThreadPool(int threads) `
返回一个带有固定threads线程数的线程池,所有空闲线程均被永远保留。如果提交的任务多于空闲线程的数量,那么把得不到服务的任务暂时安置到队列中,直到有空闲线程为止,再运行它们。
* `public static ExecutorService newSingleThreadExecutor()`
返回一个执行器,它在一个单个的线程中依次执行各个任务。
* `newScheduledThreadPool`
* `newSingleThreadScheduledExecutor`
<br/>
**2. 将任务提交给线程池的方法**
将任务提交给线程池即将Runnable对象,或Callable对象提交给线程池。
* java.util.concurrent.ExecutorService 5.0
* `Future<T> submit(Callable<T> task)`
提交指定的任务到线程池中去执行。返回的Future对象将在计算结果准备好的时候得到Callable对象。
* `Future<T> submit(Runnable task, T result)`
提交指定的任务到线程池中去执行。任务完成时,调用`get`会返回指定的对result对象。
* `Future<?> submit(Runnable task)`
提交指定的任务到线程池中去执行。可以调用`isDone`、`cancel`、`isCancel`方法。但是调用`get`方法完成任务时,只返回`null`。
* `void shutdown()`
关闭服务。完成所有已经提交到线程池中的任务后才关闭,但是不再接收新的任务。
* `List<Runnable> shutdownNow()`
关闭服务。取消线程池中尚未得到服务的线程,并试图中断正在运行的线程。
* java.util.concurrrent.ThreadPoolExecutor 5.0
* `int getLargersPoolSize()`
返回线程池生命周期中的最多活跃线程数。
<br/>
# 3. 线程池使用步骤
```
// 1. 创建线程池
ExecutorService pool = Executors.newCachedThreadPool();
// 2. 提交任务到线程池,counter为实现Runnable,或Callable接口的对象
Future<Integer> task = pool.submit(counter);
// 3. 如果要取消一个任务,则调用cancel方法
task.cancel(true);
// 4. 完成所有任务后,关闭线程池
pool.shutdown();
```
案例演示:统计有多个文件存在某一个关键字。
```
public class ThreadPoolTest {
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)) {
System.out.print("请输入一个目录: ");
String directory = in.nextLine();
System.out.print("请输入一个关键字: ");
String keyword = in.nextLine();
//创建线程池
ExecutorService pool = Executors.newCachedThreadPool();
MatchCounterPool counter = new MatchCounterPool(new File(directory), keyword, pool);
//将Callable对象提交给Future对象
Future<Integer> task = pool.submit(counter);
try {
//15 matching files.
System.out.println(task.get() + " matching files.");
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown(); //关闭线程池
}
}
}
/**
* 此任务计算包含给定关键字的目录及其子目录中的文件
*/
class MatchCounterPool implements Callable<Integer> {
private File directory;
private String keyword;
private ExecutorService pool;
private int count;
public MatchCounterPool(File directory, String keyword, ExecutorService pool) {
this.directory = directory;
this.keyword = keyword;
this.pool = pool;
}
@Override
public Integer call() throws Exception {
System.out.println("......." + Thread.currentThread().getName() + ".......");
count = 0;
try {
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file : files) {
System.out.println(Thread.currentThread().getName() + "->" + file.getName());
if (file.isDirectory()) {
MatchCounterPool counter = new MatchCounterPool(file, keyword, pool);
//将Callable对象提交给Future对象
Future<Integer> result = pool.submit(counter);
results.add(result);
System.out.println(Thread.currentThread().getName() + "->" + "Count0=" + count);
} else {
if (search(file)) {
count++;
System.out.println(Thread.currentThread().getName() + "->" + "Count1=" + count);
}
}
}
for (Future<Integer> result : results) {
System.out.println(Thread.currentThread().getName() + "->Count2=" + count);
try {
//Thread-0运行到这里,发现Thread-1和Thread-2还没计算完成,于是阻塞Thread-0
//等到Thread-1和Thread-2计算完成后,解除Thread-0的阻塞状态,并由Thread-0统计它们三个的计算结果
count += result.get();
System.out.println(Thread.currentThread().getName() + "->Count3=" + count);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
} catch (InterruptedException e) {
}
return count;
}
/**
* 在文件中搜索给定的关键字
*
* @param file ,被搜索的文件
* @return,返回ture,则表明在文件中包含该关键字
*/
public boolean search(File file) {
try {
try (Scanner in = new Scanner(file, "UTF-8")) {
boolean found = false;
while (!found && in.hasNextLine()) {
String line = in.nextLine();
if (line.contains(keyword)) found = true;
}
return found;
}
} catch (IOException e) {
return false;
}
}
}
```
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤