线程池关闭方式
- 线程池创建
- 只需要执行shutdown就可以优雅关闭
- 先上代码
- 分析一下代码关键操作
线程池创建
线程池创建实例参数我就不说了 自己去搜索很多 我个人比较喜欢文章中的实用方式
只需要执行shutdown就可以优雅关闭
先上代码
package com.zxd.concurrent;
import com.google.common.collect.Lists;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class TestTHreadI {
@Test
public void test() {
// TODO 如何正确优雅简单的关闭线程池 ,无须其他多余操 ;创建线程池我选择用这种方法比较可靠
ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//1.接收处理数据的结果集合
List<Integer> resList = Lists.newCopyOnWriteArrayList();
// List<Integer> resList = Lists.newArrayList();
long start = System.currentTimeMillis();
for (int i = 0; i < 200000; i++) {
// Future<?> future = executor.submit(new Task(i,reList));
executor.execute(new Task(i, resList));
}
executor.shutdown();
int j = 0;
while (true) {
//5.这里是等线程池彻底关闭以后做的判断 保证所有线程池已经全部关闭退出while循环
if (executor.isTerminated()) {
System.out.println("所有线程已经运行完毕:" + j);
break;
}
// 为避免一直循环 加个睡眠
try {
//如果执行shutdown方法没有关闭的线程池线程池会尝试关闭
System.out.println("尝试关闭线程次数:" + j);
j++;
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("处理时间:" + (end - start));
System.out.println("【完成的总线程数】:" + resList.size());
}
@Test
public void test33() throws Exception {
// TODO 如何正确优雅简单的关闭线程池 ,无须其他多余操 ;创建线程池我选择用这种方法比较可靠
ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
long start = System.currentTimeMillis();
List<Integer> queryList = Lists.newArrayList();
List<Future<Integer>> futureList = Lists.newArrayList();
List<Future<List<Integer>>> futureList1 = Lists.newArrayList();
for (int i = 0; i < 200000; i++) {
queryList.add(i);
// Future<Integer> future = executor.submit(new ThreadCall(i));
// futureList.add(future);
}
//LIST自对应分组,200个是分组200批 每组2000个去处理
// List<List<Integer>> nList = averageAssign(queryList,200);
//guava 自带的分组处理LIST 每2000个分到一组同上面的分组一样效果
List<List<Integer>> nList = Lists.partition(queryList, 2000);
List<Integer> totalList = Lists.newArrayList();
for (List<Integer> list1 : nList) {
Future<List<Integer>> future = executor.submit(new ThreadCall2(list1));
futureList1.add(future);
}
//FIXME 第一种是不分批处理list
// if(futureList!=null&&futureList.size()>0){
// for(Future<Integer> future:futureList){
// totalList.add(future.get());
// }
// }
//fixme 第二种分批处理list
if (futureList1 != null && futureList1.size() > 0) {
for (Future<List<Integer>> listFuture : futureList1) {
totalList.addAll(listFuture.get());
}
}
executor.shutdown();
int j = 0;
while (true) {
//5.这里是等线程池彻底关闭以后做的判断 保证所有线程池已经全部关闭退出while循环
if (executor.isTerminated()) {
System.out.println("所有线程已经运行完毕:" + j);
break;
}
// 为避免一直循环 加个睡眠
try {
//如果执行shutdown方法没有关闭的线程池线程池会尝试关闭
System.out.println("尝试关闭线程次数:" + j);
j++;
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("处理时间:" + (end - start));
System.out.println("处理的数据返回结果" + totalList.size());
}
class Task implements Runnable {
int num;
List<Integer> list;
public Task(int num, List<Integer> list) {
this.num = num;
this.list = list;
}
@Override
public void run() {
list.add(num);
}
}
class TestCall implements Callable<Integer> {
private int num;
public TestCall(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
return num * 10;
}
}
class ThreadCall2 implements Callable<List<Integer>> {
List<Integer> list;
public ThreadCall2(List<Integer> list) {
this.list = list;
}
@Override
public List<Integer> call() throws Exception {
List<Integer> reList = Lists.newArrayList();
for (Integer i : list) {
reList.add(i * 10);
}
return reList;
}
}
public static <T> List<List<T>> averageAssign(List<T> source, int n) {
List<List<T>> result = new ArrayList<>();
//(先计算出余数)
int remainder = source.size() % n;
//然后是商
int number = source.size() / n;
//偏移量
int offset = 0;
for (int i = 0; i < n; i++) {
List<T> value;
if (remainder > 0) {
value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
remainder--;
offset++;
} else {
value = source.subList(i * number + offset, (i + 1) * number + offset);
}
result.add(value);
}
return result;
}
}
分析一下代码关键操作
1、关于 CopyOnWriteArrayList 这个是JUC中提供的线程安全的集合处理类,
普通的ArrayList如果在操作多线程接收或者处理业务数据会丢失或者抛异常这里不在赘述,如果遇到此类的坑可以去网上搜索。
2、关于我们要接收线程处理业务数据收集结果上面的是一种方法以外
我们都知道实现线程类一个是Runable 另一个是CallAble
当我们实现callable接口时候我们可以接收到线程返回结果
使用FUTURE 对象去接收callable返回的结果
3、future 接收返回结果一定要避开一个坑
不要直接在线程接收完返回结果就直接使用
futrue.get()方法
这个方法会阻塞线程,造成其他线程等待。
一定要用一个List<future<?>>去接收我们的线程返回的futrue对象
4、当我们的线程都处理完以后再遍历
所有future对象 future.get()操作处理数据