ThreadPoolExecutor线程池
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class ApiRecvServiceImpl{
/**
corePoolSize—要保留在池中的线程数,即使它们是空闲的,除非设置了allowCoreThreadTimeOut
maximumPoolSize—池中允许的最大线程数
keepAliveTime -当线程数大于内核时,这是多余空闲线程在终止前等待新任务的最大时间。
unit - keepAliveTime参数的时间单位
workQueue——用于在任务执行前保存任务的队列。此队列将仅保存由execute方法提交的可运行任务。
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
*/
private final ThreadPoolExecutor tempExecutor = new ThreadPoolExecutor(20,40,1000,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
public void apiRecvStudyRecord() {
try {
Runnable task1=new Runnable() {
public void run() {
List<BaseInfosModel> tempBaseInfos= pServiceImpl.getBaseInfos();
for(ApiRecvModel info:infos) {
if(tempBaseInfos!=null&& !tempBaseInfos.isEmpty()) {
for(BaseInfosModel tempBaseInfo:tempBaseInfos) {
if(info.sysID.equals(tempBaseInfo.sysID)) {
info.sysName=tempBaseInfo.sysName;
}
}
}
}
apDao.addStudyRecord(infos.get(0).schoolID.replace("-", "_"),infos);
}
};
tempExecutor.execute(task1); // execute不带返回值
// 带返回内容
Callable<String> task2 = new Callable<String>() {
public String call() throws Exception {
System.out.println("task2");
return "success";
}
};
Future<String> future =tempExecutor.submit(task2); // submit既可以不带返回值(返回null)也可以待返回值
try {
System.out.println(future.get());
} catch (Exception e) {
e.printStackTrace();
}
future.isDone(); // 任务是否完成
tempExecutor.isTerminated(); // 判断全部线程是否全部完成
/**
* 使用 Executors 创建线程池,容易造成资源耗尽的风险,具体如下:
* SingleThreadExecutor 和 FixedThreadPool :允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量请求,从而导致OOM(Out of Memory);
* CachedThreadPool 和 ScheduledThreadPool:允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量线程,从而导致OOM。
* */
ExecutorService threadPool = Executors.newSingleThreadExecutor();
threadPool.execute(task1);
future = threadPool.submit(task2);
try {
System.out.println(future.get());
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception err) {
loggerService.writeInfo(Thread.currentThread().getStackTrace()[1].getMethodName()+"("+JSON.toJSONString(infos)+")", err);
}
}
}
笔记来源:重温Java基础(二)之Java线程池最全详解 - 码农Academy - 博客园 (cnblogs.com),建议前往查看详细解说 // 推荐使用Guava的ThreadFactory构建ThreadFactory,自定义线程名称 方便后续排查问题 ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("mythread-pool-").build(); // 定义号线程 ExecutorService executorService = new ThreadPoolExecutor( // 核心线程数,即2个常开窗口 2, // 最大的线程数,银行所有的窗口 5, // 空闲时间 5, TimeUnit.SECONDS, // 工作队列 new LinkedBlockingQueue<>(5), // 线程工厂 threadFactory, // 拒绝策略 new ThreadPoolExecutor.AbortPolicy() );
工作队列:
更多详情,请跳转Java线程池实现原理及其在美团业务中的实践 - 美团技术团队 (meituan.com)查看,该图仅作笔记收录
拒绝策略:
countDownLatch并发处理
import java.util.concurrent.CountDownLatch;
@Component
public class BusinessServiceImpl {
public void getExamTypeCodes() {
CountDownLatch countDownLatch = new CountDownLatch(1); // 创建并确定并发数量
new Thread(new Runnable() {
@Override
public void run() {
try {
// 执行线程内容
} catch (Exception err) {
loggerService.writeInfo("处理出错", err);
} finally {
countDownLatch.countDown(); // 关闭当前线程
}
}
}).start();
}
countDownLatch.await(); // 等待全部线程完成
}
CompletableFuture并发处理
/** * 不使用指定线程池案列并发案例 */ public void threadPoolDemo() { try {// 创建两个有返回值的异步任务 CompletableFuture<String> tempCompletableFutureDemo1=CompletableFuture.supplyAsync(()->{ return "running tempCompletableFutureDemo1..."; }); CompletableFuture<String> tempCompletableFutureDemo2=CompletableFuture.supplyAsync(()->{ return "running tempCompletableFutureDemo2..."; }); // 创建两个无返回值的异步任务 CompletableFuture<Void> tempCompletableFutureDemo3=CompletableFuture.runAsync(()->{ System.out.println("tempCompletableFutureDemo3....."); }); CompletableFuture<Void> tempCompletableFutureDemo4=CompletableFuture.runAsync(()->{ System.out.println("tempCompletableFutureDemo4....."); }); // 合并任务结果 CompletableFuture<String> tempCFDResult=tempCompletableFutureDemo1.thenCombine(tempCompletableFutureDemo2,(tempA,tempB)->{ StringBuilder tempBackInfos=new StringBuilder(); tempBackInfos.append("返回结果内容:"); if(StringUtils.isNotBlank(tempA)){ tempBackInfos.append(" tempCompletableFutureDemo1(").append(tempA).append(")"); } if(StringUtils.isNotBlank(tempB)){ tempBackInfos.append(" tempCompletableFutureDemo2(").append(tempB).append(")"); } return tempBackInfos.toString(); }); // 等待有返回值的合并任务完成并返回结果 String tempBackInfo=tempCFDResult.get(); System.out.println(tempBackInfo); // 等待无返回值的任务完成 CompletableFuture.allOf(tempCompletableFutureDemo3,tempCompletableFutureDemo4).get(); } catch (Exception err) { System.out.println(err); } }
/** * 使用指定线程池案列 */ public void threadPoolDemo() { try { ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 40, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2)); // 创建两个有返回值的异步任务 CompletableFuture<String> tempCompletableFutureDemo1=CompletableFuture.supplyAsync(()->{ return "running tempCompletableFutureDemo1..."; },executor); CompletableFuture<String> tempCompletableFutureDemo2=CompletableFuture.supplyAsync(()->{ return "running tempCompletableFutureDemo2..."; },executor); // 创建两个无返回值的异步任务 CompletableFuture<Void> tempCompletableFutureDemo3=CompletableFuture.runAsync(()->{ System.out.println("tempCompletableFutureDemo3....."); },executor); CompletableFuture<Void> tempCompletableFutureDemo4=CompletableFuture.runAsync(()->{ System.out.println("tempCompletableFutureDemo4....."); },executor); // 合并任务结果 CompletableFuture<String> tempCFDResult=tempCompletableFutureDemo1.thenCombine(tempCompletableFutureDemo2,(tempA,tempB)->{ StringBuilder tempBackInfos=new StringBuilder(); tempBackInfos.append("返回结果内容:"); if(StringUtils.isNotBlank(tempA)){ tempBackInfos.append(" tempCompletableFutureDemo1(").append(tempA).append(")"); } if(StringUtils.isNotBlank(tempB)){ tempBackInfos.append(" tempCompletableFutureDemo2(").append(tempB).append(")"); } return tempBackInfos.toString(); }); // 等待有返回值的合并任务完成并返回结果 String tempBackInfo=tempCFDResult.get(); System.out.println(tempBackInfo); // 等待无返回值的任务完成 CompletableFuture.allOf(tempCompletableFutureDemo3,tempCompletableFutureDemo4).get();
executor.shutdown(); // 调用此方法后,ExecutorService
将不再接受新的任务,但已提交的任务会继续执行直到完成。
executor.shutdownNow(); // 这个方法试图停止所有正在执行的任务,并返回等待执行的任务列表。
/**
它会等待直到以下三种情况之一发生:所有任务执行完成;到达指定的超时时间;当前线程被中断。
*/
executor.awaitTermination(); // 在调用shutdown()或shutdownNow()之后,你可以使用awaitTermination()方法来阻塞当前线程直到所有任务完成执行或指定的超时时间过去。
} catch (Exception err) { System.out.println(err); } }
注:不指定线程池内部使用的还是线程池处理,只不过用的是内部默认的线程池。
其余拓展
// 使用CompletableFuture进行串行处理,重点:1、thenRun\thenRunAsync串行处理;2、get/join的处理 CompletableFuture<Void> tempCompletableFutureDemo=CompletableFuture.runAsync(()->{ try { Thread.sleep(500); } catch (Exception ignored) {} System.out.println("1....."); }).thenRunAsync(()->{ try { Thread.sleep(1000); } catch (Exception ignored) {} System.out.println("2....."); }).thenRun(()->{ try { Thread.sleep(100); } catch (Exception ignored) {} System.out.println("3....."); }).thenRunAsync(()->{ try { Thread.sleep(100); } catch (Exception ignored) {} System.out.println("4....."); }); // 等待结果完成() // tempCompletableFutureDemo.get(); // 此时与get一样的,内部也是使用了waitingGet,不过也有应用区别,如下:“get与join的区别应用” tempCompletableFutureDemo.join(); System.out.println("运行结束"); // get与join的区别应用 CompletableFuture<String> tempCompletableFutureDemo1=CompletableFuture.supplyAsync(()-> { try { Thread.sleep(500); } catch (Exception ignored) {} return "返回第一个"; }); CompletableFuture<String> tempCompletableFutureDemo2=CompletableFuture.supplyAsync(()->{ try { Thread.sleep(100); } catch (Exception ignored) {} return "返回第二个"; }); // 需要等待两个同时完成。 CompletableFuture<Void> tempCFDAllResult=CompletableFuture.allOf(tempCompletableFutureDemo1,tempCompletableFutureDemo2); tempCFDAllResult.join(); // 并分别返回数据 System.out.println(tempCompletableFutureDemo1.get()); System.out.println(tempCompletableFutureDemo2.get()); System.out.println("get与join的区别应用,运行结束");
// 基础之上的串行处理。重点:thenApplyAsync串行处理以及将上一步的结果作为参数进行传递,并具备返回值
CompletableFuture<Integer> tempCompletableFutureDemo=CompletableFuture.supplyAsync(()->"这个先返回字符串类型,后面再返回其它类型的字符串,不限于int").thenApplyAsync(tempPrevResult->{
return tempPrevResult.length();
});
System.out.println(tempCompletableFutureDemo.get());
// 基础之上的串行处理。重点:thenAccept串行处理以及将上一步的结果作为参数进行传递,不具备返回值
CompletableFuture<Void> tempCompletableFutureDemo=CompletableFuture.supplyAsync(()->"这个先返回字符串类型,后面再返回其它类型的字符串,不限于int").thenAccept(tempPrevResult->{
System.out.println(tempPrevResult);
});
// anyOf,两个中的任意一个完成即可完成。 CompletableFuture<String> tempCompletableFutureDemo1=CompletableFuture.supplyAsync(()-> { try { Thread.sleep(500); } catch (Exception ignored) {} return "返回第一个"; }); CompletableFuture<String> tempCompletableFutureDemo2=CompletableFuture.supplyAsync(()->{ try { Thread.sleep(100); } catch (Exception ignored) {} return "返回第二个"; }); CompletableFuture<Object> tempCFDAnyResult=CompletableFuture.anyOf(tempCompletableFutureDemo1,tempCompletableFutureDemo2); System.out.println(tempCFDAnyResult.get()); // 这里返回第二个 System.out.println("anyOf运行结束");
各个函数使用划分可以看一下这个文章,在这里不做笔记记录:Java并发基础:CompletableFuture全面解析 (baidu.com)
CyclicBarrier线程池
import java.util.Set; import java.util.concurrent.*; public class Main { //保存每个学生的平均成绩 private ConcurrentHashMap<String, Integer> map=new ConcurrentHashMap<String,Integer>(); private ExecutorService threadPool= Executors.newFixedThreadPool(3); // 设置3个线程屏障,一道达到3个,进行await()释放一轮,注:如果不满足3个将会无法结束 private CyclicBarrier cb=new CyclicBarrier(3,()->{ int result=0; Set<String> set = map.keySet(); for(String s:set){ result+=map.get(s); } System.out.println("三人平均成绩为:"+(result/3)+"分"); }); public void count(){ for(int i=0;i<8;i++){ threadPool.execute(new Runnable(){ @Override public void run() { //获取学生平均成绩 int score=(int)(Math.random()*40+60); map.put(Thread.currentThread().getName(), score); System.out.println(Thread.currentThread().getName() +"同学的平均成绩为:"+score); try { //执行完运行await(),等待所有学生平均成绩都计算完毕 cb.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }); } } public static void main(String[] args) { Main cb=new Main(); cb.count(); System.out.println("Hello world!"); } }
输出:
Hello world! pool-1-thread-3同学的平均成绩为:99 pool-1-thread-2同学的平均成绩为:69 pool-1-thread-1同学的平均成绩为:82 三人平均成绩为:83分 pool-1-thread-1同学的平均成绩为:74 pool-1-thread-2同学的平均成绩为:71 pool-1-thread-3同学的平均成绩为:74 三人平均成绩为:73分 pool-1-thread-3同学的平均成绩为:96 pool-1-thread-1同学的平均成绩为:67
注:最后这个无法结束
线程中断处理
// 获取所有活动线程的映射 Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); // 遍历映射,查找具有指定名称的线程 for (Thread t : allStackTraces.keySet()) { if (t.getName().equals("线程名称")) { t.interrupt(); // 中断线程 } }
ThreadLocal
详情可以查看博客:
ThreadLocal 的用途与用法全解析:Java 多线程开发的利器_threadlocal的用途和用法-CSDN博客
ThreadLocal必须需要使用remove方法进行释放吗?_threadlocal需要手动remove吗-CSDN博客
ThreadLocal 是 Java 中的一个类(java.lang.ThreadLocal),用于为每个线程提供独立的变量副本。 核心思想:每个线程拥有自己的存储空间,互不干扰,避免了线程间的数据竞争和同步开销。 工作原理:ThreadLocal 内部维护一个 ThreadLocalMap,以线程为键(Thread 对象),存储对应的值。当线程调用 get() 或 set() 方法时,操作的是当前线程的独立副本。 内存结构:每个 Thread 对象有一个 ThreadLocalMap。ThreadLocalMap 以 ThreadLocal 对象为键,存储具体值。 优势: 线程安全:无需加锁即可实现数据隔离。 性能提升:避免同步机制(如 synchronized)的开销。
注:线程池中的线程会被复用导致Entry未清除,需在任务结束后调用remove(),否则会存在内存泄漏。