问题总结
一、java问题汇总
-
线程池创建的问题
场景:在推荐系统开发过程中,有一个数据的获取场景,模型输出的结果必须要查redis进行映射才能得到最后的结果,redis中的数据是通过hash格式存储的,每个key对固定的field的值对应了一个结果。一次post请求要查询300次redis,为了满足时延,使用多线程分3次进行查询。
问题:创建线程池的问题,使用ThreadPoolExecutor进行线程池的创建
使用api public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } corePoolSize 核心线程数 maximumPoolSize 最大线程数 keepAliveTime 线程池中核心线程之外的其他线程空闲时存活时间 unit 存活时间的单位 BlockingQueue 阻塞队列 忽略了两个参数 线程工厂、线程池数达到最大和队列满的时候拒绝策略 默认了四种拒绝策略(可以自定义拒绝策略) ThreadPoolExecutor.AbortPolicy() 直接抛出异常RejectedExecutionException(默认策略) ThreadPoolExecutor.CallerRunsPolicy() 交给主线程运行 ThreadPoolExecutor.DiscardPolicy() 忽略后面来的任务 ThreadPoolExecutor.DiscardOldestPolicy() 忽略队列中队首中的任务 错误使用方式 ThreadPoolExecutor(100,100,100L, TimeUnit.SECOND,new LinkedBlockingQueue<>(100)) 忽略了默认的拒绝策略,现网提交线程任务查询redis次数增加时直接报错。这种方式能提交200个任务,100个执行,100个阻塞队列中等待。 修改后: 使用Executors.newCachedThreadPool()的方式来创建线程池。默认调用的 new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>()) SynchronousQueue其实是不进行阻塞,直接创建新线程执行任务
绘图分析线程池的线程创建流程
-
并发编程包中的计数器
CountDownLatch用法
public static void main(String[] args) { ArrayList<Integer> tasks = new ArrayList<>(); CountDownLatch count = new CountDownLatch(tasks.size()); for (Integer task : tasks) { pool.execute(() -> { try { //业务执行代码 } finally { count.countDown(); } }); } try { //count.await(); boolean wait = count.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { //线程异常中断的异常 } } }
CountDownLatch先定义一个初始化的大小,每个线程执行完后减1,主线程调用await方法等待计数器减为0。如果设置了超时时间,await只等待10s,会返回一个bool值告诉主线程线程组的任务有没有执行完成。