SpringBoot多任务并⾏+线程池处理的实现
前⾔
前⼏篇⽂章着重介绍了后端服务数据库和多线程并⾏处理优化,并⽰例了改造前后的伪代码逻辑。当然了,优化是⽆⽌境的,前⼈栽树后⼈乘凉。作为
我们开发者来说,既然站在了巨⼈的肩膀上,就要写出更加优化的程序。
改造
理论上讲,线程越多程序可能更快,但是在实际使⽤中我们需要考虑到线程本⾝的创建以及销毁的资源消耗,以及保护操作系统本⾝的⽬的。我们通常
需要将线程限制在⼀定的范围之类,线程池就起到了这样的作⽤。
程序逻辑
多任务并⾏+线程池处理.png
⼀张图能解决的问题,就应该尽可能的少BB,当然底层原理性的东西还是需要⼤家去记忆并理解的。
Java 线程池
Java通过Executors提供四种线程池,分别为:
1. newCachedThreadPool创建⼀个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若⽆可回收,则新建线程。
2. newFixedThreadPool 创建⼀个定长线程池,可控制线程最⼤并发数,超出的线程会在队列中等待。
3. newScheduledThreadPool 创建⼀个定长线程池,⽀持定时及周期性任务执⾏。
4. newSingleThreadExecutor 创建⼀个单线程化的线程池,它只会⽤唯⼀的⼯作线程来执⾏任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执
⾏。
优点
1. 重⽤存在的线程,减少对象创建、消亡的开销,性能佳。
2. 可有效控制最⼤并发线程数,提⾼系统资源的使⽤率,同时避免过多资源竞争,避免堵塞。
3. 提供定时执⾏、定期执⾏、单线程、并发数控制等功能。
代码实现
⽅式⼀(CountDownLatch)
/**
* 多任务并⾏+线程池统计
* 创建时间  2018年4⽉17⽇
*/
public class StatsDemo {
final static SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
final static String startTime = sdf.format(new Date());
/**
* IO密集型任务 = ⼀般为2*CPU核⼼数(常出现于线程中:数据库数据交互、⽂件上传下载、⽹络数据传输等等)
* CPU密集型任务 = ⼀般为CPU核⼼数+1(常出现于线程中:复杂算法)
* 混合型任务 = 视机器配置和复杂度⾃测⽽定
*/
private static int corePoolSize = Runtime().availableProcessors();
/**
* public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
*              TimeUnit unit,BlockingQueue<Runnable> workQueue)
* corePoolSize⽤于指定核⼼线程数量
* maximumPoolSize指定最⼤线程数
* keepAliveTime和TimeUnit指定线程空闲后的最⼤存活时间
* workQueue则是线程池的缓冲队列,还未执⾏的线程会在队列中等待
* 监控队列长度,确保队列有界
* 不当的线程池⼤⼩会使得处理速度变慢,稳定性下降,并且导致内存泄露。如果配置的线程过少,则队列会持续变⼤,消耗过多内存。
* ⽽过多的线程⼜会由于频繁的上下⽂切换导致整个系统的速度变缓——殊途⽽同归。队列的长度⾄关重要,它必须得是有界的,这样如果线程池不堪重负了它可以暂时拒绝掉新的请求。  * ExecutorService 默认的实现是⼀个⽆界的 LinkedBlockingQueue。
*/
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1000));
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
//使⽤execute⽅法
latch.await();// 等待所有⼈任务结束
System.out.println("所有的统计任务执⾏完成:" + sdf.format(new Date()));
}
static class Stats implements Runnable {
String statsName;
int runTime;
CountDownLatch latch;
public Stats(String statsName, int runTime, CountDownLatch latch) {
this.statsName = statsName;
this.runTime = runTime;
this.latch = latch;
}
public void run() {
try {
System.out.println(statsName+ " do stats begin at "+ startTime);
//模拟任务执⾏时间
Thread.sleep(runTime);
System.out.println(statsName + " do stats complete at "+ sdf.format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
⽅式⼆(Future)
/**
* 多任务并⾏+线程池统计
* 创建时间  2018年4⽉17⽇
*/
public class StatsDemo {
final static SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
final static String startTime = sdf.format(new Date());
/**
* IO密集型任务 = ⼀般为2*CPU核⼼数(常出现于线程中:数据库数据交互、⽂件上传下载、⽹络数据传输等等)
* CPU密集型任务 = ⼀般为CPU核⼼数+1(常出现于线程中:复杂算法)
* 混合型任务 = 视机器配置和复杂度⾃测⽽定
*/
private static int corePoolSize = Runtime().availableProcessors();
/**
* public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
*              TimeUnit unit,BlockingQueue<Runnable> workQueue)
* corePoolSize⽤于指定核⼼线程数量
* maximumPoolSize指定最⼤线程数
* keepAliveTime和TimeUnit指定线程空闲后的最⼤存活时间
* workQueue则是线程池的缓冲队列,还未执⾏的线程会在队列中等待
* 监控队列长度,确保队列有界
* 不当的线程池⼤⼩会使得处理速度变慢,稳定性下降,并且导致内存泄露。如果配置的线程过少,则队列会持续变⼤,消耗过多内存。
* ⽽过多的线程⼜会由于频繁的上下⽂切换导致整个系统的速度变缓——殊途⽽同归。队列的长度⾄关重要,它必须得是有界的,这样如果线程池不堪重负了它可以暂时拒绝掉新的请求。  * ExecutorService 默认的实现是⼀个⽆界的 LinkedBlockingQueue。
*/
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1000));
public static void main(String[] args) throws InterruptedException {
List<Future<String>> resultList = new ArrayList<Future<String>>();
//使⽤submit提交异步任务,并且获取返回值为future
resultList.add(executor.submit(new Stats("任务A", 1000)));
resultList.add(executor.submit(new Stats("任务B", 1000)));
resultList.add(executor.submit(new Stats("任务C", 1000)));
resultList.add(executor.submit(new Stats("任务D", 1000)));
resultList.add(executor.submit(new Stats("任务E", 1000)));
//遍历任务的结果
for (Future<String> fs : resultList) {
try {
System.out.());//打印各个线任务执⾏的结果,调⽤() 阻塞主线程,获取异步任务的返回结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
//启动⼀次顺序关闭,执⾏以前提交的任务,但不接受新任务。如果已经关闭,则调⽤没有其他作⽤。
executor.shutdown();
}
springboot推荐算法}
System.out.println("所有的统计任务执⾏完成:" + sdf.format(new Date()));
}
static class Stats implements Callable<String> {
String statsName;
int runTime;
public Stats(String statsName, int runTime) {
this.statsName = statsName;
this.runTime = runTime;
}
public String call() {
try {
System.out.println(statsName+ " do stats begin at "+ startTime);
//模拟任务执⾏时间
Thread.sleep(runTime);
System.out.println(statsName + " do stats complete at "+ sdf.format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
return call();
}
}
}
执⾏时间
以上代码,均是伪代码,下⾯是2000+个学⽣的真实测试记录。
2018-04-17 17:42:29.284 INFO  测试记录81e51ab031eb4ada92743ddf66528d82-单线程顺序执⾏,花费时间:3797
2018-04-17 17:42:31.452 INFO  测试记录81e51ab031eb4ada92743ddf66528d82-多线程并⾏任务,花费时间:2167
2018-04-17 17:42:33.170 INFO  测试记录81e51ab031eb4ada92743ddf66528d82-多线程并⾏任务+线程池,花费时间:1717以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。