笔记

万物寻其根,通其堵,便能解其困。
  博客园  :: 新随笔  :: 管理

Spring boot 线程池

Posted on 2024-03-30 16:40  草妖  阅读(12)  评论(0)    收藏  举报

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到底安全不?

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(),否则会存在内存泄漏。