时间:2021-05-02
前言
前几篇文章着重介绍了后端服务数据库和多线程并行处理优化,并示例了改造前后的伪代码逻辑。当然了,优化是无止境的,前人栽树后人乘凉。作为我们开发者来说,既然站在了巨人的肩膀上,就要写出更加优化的程序。
springboot开发案例之jdbctemplate批量操作
springboot开发案例之countdownlatch多任务并行处理
改造
理论上讲,线程越多程序可能更快,但是在实际使用中我们需要考虑到线程本身的创建以及销毁的资源消耗,以及保护操作系统本身的目的。我们通常需要将线程限制在一定的范围之类,线程池就起到了这样的作用。
程序逻辑
多任务并行+线程池处理.png
一张图能解决的问题,就应该尽可能的少bb,当然底层原理性的东西还是需要大家去记忆并理解的。
java 线程池
java通过executors提供四种线程池,分别为:
优点
代码实现
方式一(countdownlatch)
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 /** * 多任务并行+线程池统计 * 创建时间 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.getruntime().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方法 executor.execute(new stats("任务a", 1000, latch)); executor.execute(new stats("任务b", 1000, latch)); executor.execute(new stats("任务c", 1000, latch)); executor.execute(new stats("任务d", 1000, latch)); executor.execute(new stats("任务e", 1000, latch)); 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())); latch.countdown();//单次任务结束,计数器减一 } catch (interruptedexception e) { e.printstacktrace(); } } } }方式二(future)
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /** * 多任务并行+线程池统计 * 创建时间 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.getruntime().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.println(fs.get());//打印各个线任务执行的结果,调用future.get() 阻塞主线程,获取异步任务的返回结果 } catch (interruptedexception e) { e.printstacktrace(); } catch (executionexception e) { e.printstacktrace(); } finally { //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。 executor.shutdown(); } } 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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.52itstyle.com/archives/2705/
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
多任务(并行和并发)在讲协程之前,先谈谈多进程、多线程、并行和并发。对于单核处理器,多进程实现多任务的原理是让操作系统给一个任务每次分配一定的CPU时间片,然后
线程池(ThreadPool)是并行执行任务收集的实用工具。随着CPU引入适合于应用程序并行化的多核体系结构,线程池的作用正日益显现。通过ThreadPoolE
Java线程池ExecutorService1.线程池 1.1什么情况下使用线程池单个任务处理的时间比较短.将需处理的任务的数量大.1.2使用线程池的好处减少在
我们怎么让一个Python程序里边实现多任务呢?实现多任务可以有多种方式,这里我们先了解使用线程的方式实现多任务。线程是实现多任务的一种的手段。其实用的是thr
什么是线程池线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默