最近想研究下线程的使用,但是如果自己创建一个线程,太费系统性能 ,所以打算自己写个线程池
参考博客:https://www.cnblogs.com/dolphin0520/p/3932921.html
参考书籍:<<java并发编程的艺术>>
一 Java中的ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
在ThreadPoolExecutor类中提供了四个构造方法:
public class ThreadPoolExecutor extends AbstractExecutorService { ..... public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); ... }
从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
下面解释下一下构造器中各个参数的含义:
- corePoolSize: (线程池的基本大小) 当提交一个任务到线程池时,线程池会创建一个线程来执行任务,如果线程池的有空闲线程但是数量小于 corePoolSize时,也会创建一个线程来执行,等到需要执行的任务数 大于线程池基本大小的时候就不会再创建, 如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
- maximumPoolSize:(线程池最大数量):线程池允许创建的最大线程数,如果队列满了.并且已经创建的线程数量小于最大线程数,则线程池会创建新的线程执行任务.值得注意的是,如果使用了无界的任务队列这个参数就没有效果了
- keepAliveTime:(线程活动保持时间) :表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,就是说线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;如果任务很多,并且每个任务执行的时间比较短时,可以调大时间,提高线程的利用率
- TimeUnit:
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小时 TimeUnit.MINUTES; //分钟 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //纳秒
- workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
-
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列
- RejectedExecutionHandler handler(饱和策略):表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
- ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字,代码如下
new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
以上是线程池的基本概念
二 线程池内部原理实现
1.在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。
workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:
1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
2.线程池的状态
runState
// runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
3.线程池获取资源情况
要清楚corePoolSize和maximumPoolSize的含义;
其次,要知道Worker是用来起到什么作用的;
要知道任务提交给线程池之后的处理策略,这里总结一下主要有4点:
- 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
- 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
- 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
4.线程池的关闭
ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:
- shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
- shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
线程池关闭的原理是遍历线程池中的工作先,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法停止
shutdownNow首先将线程池的状态设置为stop,然后尝试停止所有正在执行或者暂停任务的线程
shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有正在执行的任务的线程
只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务
都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪
一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭
线程池,如果任务不一定要执行完,则可以调用shutdownNow方法
5.线程的提交
execute()用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
submit()用户提交需要返回值的任务,线程池会返回一个future类型的对象,通过这个对象判断是否执行成功,future.get()方法 get方法会阻塞当前线程知道任务完成,,而是用get(long time,timeuitl unit )会阻塞一段时间后返回,这个时间有可能没有执行完任务
Future<Object> future = executor.submit(harReturnValuetask); try { Object s = future.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); }
三 使用实例
/** * corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线 * 程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任 * 务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法, * 线程池会提前创建并启动所有基本线程。 */ private static int corePoolSize =5; /** * (线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并 * 且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如 * 果使用了无界的任务队列这个参数就没什么效果 */ private static int maximumPoolSize =10; /** * (线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以, * 如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率 * @param args */ private static long keepAliveTime =200; public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5)); for (int i = 0; i <20 ; i++) { executor.submit(new Mytask(i)); System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+ executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount()); } executor.shutdown(); } } class Mytask implements Runnable{ private int a; public Mytask(int a) { this.a = a; } @Override public void run() { System.out.println(a+"任务执行"); try { Thread.currentThread().sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a+"执行完毕"); }
/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61838:/Applications/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/tools.jar:/Users/xuyuanfang/IdeaProjects/threaddemo/target/classes:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-web/2.1.3.RELEASE/spring-boot-starter-web-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter/2.1.3.RELEASE/spring-boot-starter-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot/2.1.3.RELEASE/spring-boot-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-autoconfigure/2.1.3.RELEASE/spring-boot-autoconfigure-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-logging/2.1.3.RELEASE/spring-boot-starter-logging-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/xuyuanfang/apps/maven_jar/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/logging/log4j/log4j-to-slf4j/2.11.2/log4j-to-slf4j-2.11.2.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/logging/log4j/log4j-api/2.11.2/log4j-api-2.11.2.jar:/Users/xuyuanfang/apps/maven_jar/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar:/Users/xuyuanfang/apps/maven_jar/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar:/Users/xuyuanfang/apps/maven_jar/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-json/2.1.3.RELEASE/spring-boot-starter-json-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-databind/2.9.8/jackson-databind-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-core/2.9.8/jackson-core-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.8/jackson-datatype-jdk8-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.8/jackson-datatype-jsr310-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.8/jackson-module-parameter-names-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-tomcat/2.1.3.RELEASE/spring-boot-starter-tomcat-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-el/9.0.16/tomcat-embed-el-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.16/tomcat-embed-websocket-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/hibernate/validator/hibernate-validator/6.0.14.Final/hibernate-validator-6.0.14.Final.jar:/Users/xuyuanfang/apps/maven_jar/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar:/Users/xuyuanfang/apps/maven_jar/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/classmate/1.4.0/classmate-1.4.0.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-web/5.1.5.RELEASE/spring-web-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-beans/5.1.5.RELEASE/spring-beans-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-webmvc/5.1.5.RELEASE/spring-webmvc-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-aop/5.1.5.RELEASE/spring-aop-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-context/5.1.5.RELEASE/spring-context-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-expression/5.1.5.RELEASE/spring-expression-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/projectlombok/lombok/1.18.6/lombok-1.18.6.jar:/Users/xuyuanfang/apps/maven_jar/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-core/5.1.5.RELEASE/spring-core-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-jcl/5.1.5.RELEASE/spring-jcl-5.1.5.RELEASE.jar com.xiaodao.threaddemo.thread.ExecutorsTest 0任务执行 Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@e9e54c2 rejected from java.util.concurrent.ThreadPoolExecutor@65ab7765[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0] 线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 1任务执行 线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 2任务执行 线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 3任务执行 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 4任务执行 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) 线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 10任务执行 线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 11任务执行 12任务执行 线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) 13任务执行 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) 线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 14任务执行 at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112) at com.xiaodao.threaddemo.thread.ExecutorsTest.main(ExecutorsTest.java:41) 1执行完毕 4执行完毕 0执行完毕 2执行完毕 8任务执行 3执行完毕 9任务执行 7任务执行 6任务执行 13执行完毕 5任务执行 11执行完毕 14执行完毕 12执行完毕 10执行完毕 9执行完毕 5执行完毕 8执行完毕 7执行完毕 6执行完毕 Process finished with exit code 130 (interrupted by signal 2: SIGINT)
我们可以看到当执行线程大于corePoolSize 时就会将任务放入缓存队列中取,但是我们把任务数放大程序就会拒绝执行,并报错
当我们换一个饱和策略就不会报错,工作中.
public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i <20 ; i++) { executor.submit(new Mytask(i)); System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+ executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount()); } executor.shutdown(); }
使用Executors类中提供的几个静态方法来创建线程池:
不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池 Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
一下是这个三个方法的具体实现
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。
newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。
四 配置线程池的大小
要想合理的配置线程池,首先要分析任务的特性,可以重以下几个点击来分析:
- 任务的性质:CPU密集型任务,IO密集型任务,和混合型任务
- 任务的优先级:高 中和低
- 任务的执行时间:长 中和短
- 任务的依赖性:是否依赖其他资源,如,数据连接,调用别的系统接口
CPU密集型任务尽可能配置小的线程,如配置Ncpu+1个线程的线程池
IO密集型任务线程并不是一直在执行任务:应该配置更多的线程,如 2* ncpu
可以通过 Runtime.getRuntime().availableProcessors()获取当前设备的cup个数
依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU
在spring中使用线程池
目前使用spring 中的线程池有点问题.自定义的线程池倒是可以使用,此处贴出代码
@Configuration public class TheadPoolTask { /** * 获取线程池 * * @return */ @Bean public ThreadPoolExecutor threadPoolTaskExecutor(){ ThreadPoolExecutor executor = new ThreadPoolExecutor(12, 16, 12, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5), new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } }
测试类:
@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(locations = {"/spring-context.xml","/spring-context-jedis.xml","/spring-context-shiro.xml"}) public class ThreadTest { @Resource(name = "threadPoolTaskExecutor") private ThreadPoolExecutor taskExecutor; public static void main(String[] args) { System.out.println(Runtime.getRuntime().availableProcessors()); } @Test public void test01(){ try { for (int i = 0; i <25 ; i++) { taskExecutor.submit(new Test01(i)); System.out.println("线程池中线程数目:"+taskExecutor.getPoolSize()+",队列中等待执行的任务数目:"+ taskExecutor.getQueue().size()+",已执行玩别的任务数目:"+taskExecutor.getCompletedTaskCount()); } CountDownLatch c = new CountDownLatch(1); c.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } class Test01 implements Runnable{ private int a; public Test01(int a) { this.a = a; } @Override public void run() { try { System.out.println("线程开始"+ Thread.currentThread().getName()+"------"+a); Thread.sleep(5000); System.out.println("线程结束 "+ Thread.currentThread().getName()+"------"+a); } catch (InterruptedException e) { e.printStackTrace(); } } }