当系统并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要消耗大量的系统资源。
所以需要一个办法使得线程可以复用,即当线程执行完一个任务,并不被销毁,而是可以继续执行其他的任务。在Java中就可以通过线程池来实现这样的效果。本文讲述了Java中的线程池类以及如何使用线程池。
一、java中的线程池ThreadPoolExecutorThreadPoolExecutor是线程池中基础类也是最为核心的类。想要了解和合理使用线程池绕不开ThreadPoolExecutor类。下面介绍一下此类。
该类的构造函数如下
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { //... }
线程池有这么几个重要的参数
corePoolSize: 线程池里的核心线程数量
maximumPoolSize: 线程池里允许有的最大线程数量
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。 默认情况下,如果当前线程数量 > corePoolSize,多出来的线程会在keepAliveTime之后就被释放掉,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0
unit: keepAliveTime的时间单位,有7种单位
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小时 TimeUnit.MINUTES; //分钟 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //月
workQueue: 队列workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:
有界任务队列ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
无界任务队列LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
直接提交队列synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。如果进程数量已经达到最大值,则执行拒绝策略。因此使用该队列需要设置很大的maximumPoolSize,否则很容易执行拒绝策略。
threadFactory: 每当需要创建新的线程放入线程池的时候,就是通过这个线程工厂来创建的
handler: 就是说当线程,队列都满了,之后采取的策略,比如抛出异常等策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,不做任何处理
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
二、 线程池中重要的方法线程池有两个重要的操作,提交任务和关闭线程池。在讲述这两个操作之前先了解一下线程池的状态。注意!!!,线程池的状态而不是线程状态。
在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:
//当前线程池的状态,voliate 保证了线程之间的可见 volatile int runState; //创建线程池后,初始时,线程池处于此状态 static final int RUNNING = 0; /*调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕*/ static final int SHUTDOWN = 1; /*调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务*/ static final int STOP = 2; /*线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态*/ static final int TERMINATED = 3;
2.1 线程池提交任务ThreadPoolExecutor的提交操作可以使用submit和execute这两种方法
2.1.1 excute