java线程池面试题-----手写线程池实战
2021/8/1 22:35:46
本文主要是介绍java线程池面试题-----手写线程池实战,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、线程池实战例子
项目背景:需要查出一百个用户的信息,并且给他们的邮箱发送邮件,打印出最终结果
用户类
public class User { private Integer id; private String email; public User(Integer id, String email) { this.id =id; this.email =email; } public String getEmail() { return email; } }
任务类
public class Task implements Callable<String> { private Integer id; public Task(Integer id) { this.id = id; } @Override public String call() throws Exception { //调用业务方提供的查user的服务,id不同,创建任务的时候就传过来id User user = DoSomethingService.queryUser(this.id); //调用业务方提供发送邮件的服务,email不同 String result = DoSomethingService.sendUserEmail(user.getEmail()); return result; } }
提供的服务类
//业务提供的服务 public class DoSomethingService { //查询用户100ms public static User queryUser(Integer id) throws InterruptedException { //这里可以调用查询user的sql语句 Thread.sleep(100); User u= new User(id,id+"xhJaver.com"); return u; } //发送邮件50ms public static String sendUserEmail(String email) throws InterruptedException { if (email!=null){ //这里可以调用发送email的语句 Thread.sleep(50); return "发送成功"+email; }else { return "发送失败"; } } }
我们再来比较一下单线程情况下和多线程情况下相同的操作差别有多大
public class SingleVSConcurrent { public static void main(String[] args) { //我们模拟一百个用户,我们查出来这一百个用户然后再给他们发邮件 long singleStart = System.currentTimeMillis(); for (int i=0;i<100;i++){ User user = null; try { user = DoSomethingService.queryUser(i); String s = DoSomethingService.sendUserEmail(user.getEmail()); System.out.println(s); } catch (InterruptedException e) { e.printStackTrace(); } } long singleEnd = System.currentTimeMillis(); System.out.println("单线程共用了"+(singleEnd-singleStart)+"ms"); System.out.println("-------分割线-----------------分割线-----------------分割线-----------------分割线-----------------分割线----------"); long concurrentStart = System.currentTimeMillis(); //构建要做的任务列表,查询出用户来并且发送邮件 List<Task> tasks = new ArrayList<>(); for (int i=0;i<100;i++){ //传id进去构造不同的任务,业务中有可能是给你个list列表 Task task = new Task(i); tasks.add(task); } //返回任务执行结果 List<Future<String>> futures = null; //用线程池查询用户发送邮件 ExecutorService executorService = Executors.newFixedThreadPool(100); try { //是线程池执行提交的批量任务 futures = executorService.invokeAll(tasks); } catch (InterruptedException e) { e.printStackTrace(); } //关闭线程池 executorService.shutdown(); //存放任务结果的集合 List<String> results = new ArrayList<>(); //遍历这个任务执行结果 for (Future<String> result:futures) { //如果这个任务结束了 if (result.isDone()){ String s = null; try { //得到这个任务的处理结果,得不到会一直阻塞 s = result.get(); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } //将任务结果放进任务结果集合里面 results.add(s); } } //遍历任务结果的集合 for (String s:results) { System.out.println(s); } long concurrentEnd = System.currentTimeMillis(); System.out.println("多线程共用了"+(concurrentEnd-concurrentStart)+"ms"); } }
我们最终会看到输出结果为
发送成功0xhJaver.com 发送成功1xhJaver.com 发送成功2xhJaver.com 发送成功3xhJaver.com 发送成功4xhJaver.com 发送成功5xhJaver.com 发送成功6xhJaver.com 发送成功7xhJaver.com 发送成功8xhJaver.com 发送成功9xhJaver.com 发送成功10xhJaver.com 发送成功11xhJaver.com 发送成功12xhJaver.com 发送成功13xhJaver.com 发送成功14xhJaver.com 发送成功15xhJaver.com 发送成功16xhJaver.com 发送成功17xhJaver.com 发送成功18xhJaver.com 发送成功19xhJaver.com 发送成功20xhJaver.com 发送成功21xhJaver.com 发送成功22xhJaver.com 发送成功23xhJaver.com 发送成功24xhJaver.com 发送成功25xhJaver.com 发送成功26xhJaver.com 发送成功27xhJaver.com 发送成功28xhJaver.com 发送成功29xhJaver.com 发送成功30xhJaver.com 发送成功31xhJaver.com 发送成功32xhJaver.com 发送成功33xhJaver.com 发送成功34xhJaver.com 发送成功35xhJaver.com 发送成功36xhJaver.com 发送成功37xhJaver.com 发送成功38xhJaver.com 发送成功39xhJaver.com 发送成功40xhJaver.com 发送成功41xhJaver.com 发送成功42xhJaver.com 发送成功43xhJaver.com 发送成功44xhJaver.com 发送成功45xhJaver.com 发送成功46xhJaver.com 发送成功47xhJaver.com 发送成功48xhJaver.com 发送成功49xhJaver.com 发送成功50xhJaver.com 发送成功51xhJaver.com 发送成功52xhJaver.com 发送成功53xhJaver.com 发送成功54xhJaver.com 发送成功55xhJaver.com 发送成功56xhJaver.com 发送成功57xhJaver.com 发送成功58xhJaver.com 发送成功59xhJaver.com 发送成功60xhJaver.com 发送成功61xhJaver.com 发送成功62xhJaver.com 发送成功63xhJaver.com 发送成功64xhJaver.com 发送成功65xhJaver.com 发送成功66xhJaver.com 发送成功67xhJaver.com 发送成功68xhJaver.com 发送成功69xhJaver.com 发送成功70xhJaver.com 发送成功71xhJaver.com 发送成功72xhJaver.com 发送成功73xhJaver.com 发送成功74xhJaver.com 发送成功75xhJaver.com 发送成功76xhJaver.com 发送成功77xhJaver.com 发送成功78xhJaver.com 发送成功79xhJaver.com 发送成功80xhJaver.com 发送成功81xhJaver.com 发送成功82xhJaver.com 发送成功83xhJaver.com 发送成功84xhJaver.com 发送成功85xhJaver.com 发送成功86xhJaver.com 发送成功87xhJaver.com 发送成功88xhJaver.com 发送成功89xhJaver.com 发送成功90xhJaver.com 发送成功91xhJaver.com 发送成功92xhJaver.com 发送成功93xhJaver.com 发送成功94xhJaver.com 发送成功95xhJaver.com 发送成功96xhJaver.com 发送成功97xhJaver.com 发送成功98xhJaver.com 发送成功99xhJaver.com 单线程共用了18404ms -------分割线-----------------分割线-----------------分割线-----------------分割线-----------------分割线---------- 发送成功0xhJaver.com 发送成功1xhJaver.com 发送成功2xhJaver.com 发送成功3xhJaver.com 发送成功4xhJaver.com 发送成功5xhJaver.com 发送成功6xhJaver.com 发送成功7xhJaver.com 发送成功8xhJaver.com 发送成功9xhJaver.com 发送成功10xhJaver.com 发送成功11xhJaver.com 发送成功12xhJaver.com 发送成功13xhJaver.com 发送成功14xhJaver.com 发送成功15xhJaver.com 发送成功16xhJaver.com 发送成功17xhJaver.com 发送成功18xhJaver.com 发送成功19xhJaver.com 发送成功20xhJaver.com 发送成功21xhJaver.com 发送成功22xhJaver.com 发送成功23xhJaver.com 发送成功24xhJaver.com 发送成功25xhJaver.com 发送成功26xhJaver.com 发送成功27xhJaver.com 发送成功28xhJaver.com 发送成功29xhJaver.com 发送成功30xhJaver.com 发送成功31xhJaver.com 发送成功32xhJaver.com 发送成功33xhJaver.com 发送成功34xhJaver.com 发送成功35xhJaver.com 发送成功36xhJaver.com 发送成功37xhJaver.com 发送成功38xhJaver.com 发送成功39xhJaver.com 发送成功40xhJaver.com 发送成功41xhJaver.com 发送成功42xhJaver.com 发送成功43xhJaver.com 发送成功44xhJaver.com 发送成功45xhJaver.com 发送成功46xhJaver.com 发送成功47xhJaver.com 发送成功48xhJaver.com 发送成功49xhJaver.com 发送成功50xhJaver.com 发送成功51xhJaver.com 发送成功52xhJaver.com 发送成功53xhJaver.com 发送成功54xhJaver.com 发送成功55xhJaver.com 发送成功56xhJaver.com 发送成功57xhJaver.com 发送成功58xhJaver.com 发送成功59xhJaver.com 发送成功60xhJaver.com 发送成功61xhJaver.com 发送成功62xhJaver.com 发送成功63xhJaver.com 发送成功64xhJaver.com 发送成功65xhJaver.com 发送成功66xhJaver.com 发送成功67xhJaver.com 发送成功68xhJaver.com 发送成功69xhJaver.com 发送成功70xhJaver.com 发送成功71xhJaver.com 发送成功72xhJaver.com 发送成功73xhJaver.com 发送成功74xhJaver.com 发送成功75xhJaver.com 发送成功76xhJaver.com 发送成功77xhJaver.com 发送成功78xhJaver.com 发送成功79xhJaver.com 发送成功80xhJaver.com 发送成功81xhJaver.com 发送成功82xhJaver.com 发送成功83xhJaver.com 发送成功84xhJaver.com 发送成功85xhJaver.com 发送成功86xhJaver.com 发送成功87xhJaver.com 发送成功88xhJaver.com 发送成功89xhJaver.com 发送成功90xhJaver.com 发送成功91xhJaver.com 发送成功92xhJaver.com 发送成功93xhJaver.com 发送成功94xhJaver.com 发送成功95xhJaver.com 发送成功96xhJaver.com 发送成功97xhJaver.com 发送成功98xhJaver.com 发送成功99xhJaver.com 多线程共用了233ms
从输出结果可以知道 单线程共用18404ms / 150 约等于122 多线程共用233ms /150 约等于1
就相当于发用查询发送一个人的时间解决了这100个人的问题,具体的线程池核心大小数量要根据业务方面自己配置设计
二、例子重点讲解
文中注释也都清晰明了的,我在解释下几个重要的
创建任务
自定义任务类实现这个接口并且实现call方法,返回值为传入Callable的类型即可
public class Task implements Callable<String> { private Integer id; public Task(Integer id) { this.id = id; } @Override public String call() throws Exception { //调用业务方提供的查user的服务,id不同,创建任务的时候就传过来id User user = DoSomethingService.queryUser(this.id); //调用业务方提供发送邮件的服务,email不同 String result = DoSomethingService.sendUserEmail(user.getEmail()); return result; } }
构造的任务列表
//构建要做的任务列表,查询出用户来并且发送邮件 List<Task> tasks = new ArrayList<>(); for (int i=0;i<100;i++){ //传id进去构造不同的任务,业务中有可能是给你个list列表 Task task = new Task(i); tasks.add(task); }
创建线程池提交任务列表并且关闭线程池
//用线程池查询用户发送邮件 ExecutorService executorService = Executors.newFixedThreadPool(100); //返回任务执行结果 List<Future<String>> futures = null; try { //是线程池执行提交的批量任务 futures = executorService.invokeAll(tasks); } catch (InterruptedException e) { e.printStackTrace(); } //关闭线程池 executorService.shutdown();
注:提交任务的时候也可以是submit不过在这样一次提交一个任务,要是有任务列表可以用invokeAll shoutdown和shoutdownNow都可以关闭线程池。但是又有很大区别
shoutdown :不会立刻停止线程池,但是会拒绝处理新来的任务,阻塞队列中的任务等他执行完
shoutdownNow:会立刻停止线程池,拒绝处理新来的任务并且打断正在执行的线程,将阻塞队列中的任务全部清空
总的来说就是shoutdown温柔一点,shoutdownNow粗暴一点
线程池提交callable任务四种方法讲解
关于提交线程池提交callable任务有以下四种方法
invokeAll无时间参数
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
如果调没有异常发生的话都会调用成功 如果有一个有异常则有异常的调用失败,其余成功
invokeAll有时间参数
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
当全部任务执行完后超过指定时限后,直接抛出异常
invokeAny无时间参数
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
invokeAny将第一个完成的作为结果,或者调用失败则也立即终止其他所有线程。
invokeAny有时间参数
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
若设置了超时时间,未超时完成则返回正常结果,否则报错
解析任务结果
//存放任务结果的集合 List<String> results = new ArrayList<>(); //遍历这个任务执行结果 for (Future<String> result:futures) { //如果这个任务结束了 if (result.isDone()){ String s = null; try { //得到这个任务的处理结果,得不到会一直阻塞 s = result.get(); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } //将任务结果放进任务结果集合里面 results.add(s); } } //遍历任务结果的集合 for (String s:results) { System.out.println(s); }
future接口方法讲解
注:future是个jdk1.5以后出现的异步任务接口
public interface Future<V> { //若此任务已经完成或者被取消或者因为其他原因不能被取消则取消失败, //如果此任务还没有开始或者已经开始这个时候就由这个参数来觉得取消与否 // 在这个方法执行完后,isDone,iscancel总是返回true, //如果任务无法取消经常是因为任务被执行完了 boolean cancel(boolean mayInterruptIfRunning); //是否取消成功 boolean isCancelled(); //任务是否完成,正常结束取消或者异常,都返回true boolean isDone(); //得到这个任务的结果,会一直阻塞到得到结果 //如果任务被取消,则抛出CancellationException 异常 //若任务有异常,则抛出ExecutionException 异常 //若任务被中断则抛出InterruptedException 异常 V get() throws InterruptedException, ExecutionException; //得到这个任务的结果,若超过时间会抛异常,除此之外和上面那个方法一样 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
这篇关于java线程池面试题-----手写线程池实战的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南