1.为什么要用线程池
第一是减少线程创建销毁的次数,利用线程池可以让一个线程多次使用
第二是提高响应的速度,因为可以直接拿线程池里面的线程使用,减少了创建的过程(当然加入线程池里有空闲线程)
第三是便于管理
2.线程池初试
一般是通过ThreadPoolExecutor构造函数来创建线程池,但是还可以通过FixedThreadPool(固定线程数);SingleThreadExecutor(单一线程,队列先进先出),这2中允许的队列长度无限,可能会堆积大量的待运行任务;CachedThreadPool(无队列,当能够复用的时候后会优先复用线程)因为没有队列,有可能造成大量的线程被创建。所以一般通过ThreadPoolExecutor来创建线程。
public ThreadPoolExecutor(
int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
){ this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
可以发现定义了线程核心数量,线程池最大数量,存活时间,时间单位,等待执行任务的队列,线程工厂,拒绝策略.
创建线程池后,内部在运行线程为0,当一个任务需要执行的时候,先判断,如果当前线程数小于Core线程数,创建新的线程去运行,反之放入队列,等待运行,如果队列满了,检查当前线程数与最大线程数,如果当前线程数小线程数,创建新线程去运行,如果线程数大于等于最大线程数,则会报异常。可以看出,线程池能接收的最大任务数为最大线程数量+队列数量。
当定义线程池后,线程数是0,可以通过prestartAllCoreThreads/preStartCoreThread方法来预热线程池,不同的是前者会创建Core数量的线程,而后面一个则只会创建一个。
存活时间,主要作用与核心线程之外的线程,当非核心线程空闲该时间后,就会结束。不过 可以通过allowsCoreThreadTimeOut()方法可以让核心线程也会结束。
经常遇到的问题是corePoolSize/maximumPoolSize初始化时设置大小的问题。
正经的答案是看这个线程池是面向计算还是面向io的。当io比较多的时候,一般会设置cpu核心线程/(1-阻塞系数) (一般是0.8~0.9),当面向计算的时候,一般是设置成为cpu核心线程*2。
那么不怎么正常的答案便是,通过setCorePoolSize/setMaximumPoolSize方法对以及创建的线程池进行动态修改。
首先setCorePoolSize先对参数进行检查,delta是设置数减当前核心线程数的值,覆盖原来的核心线程数。
public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException(); int delta = corePoolSize - this.corePoolSize; this.corePoolSize = corePoolSize;
//当核心线程数小于当前运行的线程数,多的停掉 if (workerCountOf(ctl.get()) > corePoolSize) interruptIdleWorkers();
//当设置的值比原来的大 else if (delta > 0) { // We don't really know how many new threads are "needed". // As a heuristic, prestart enough new workers (up to new // core size) to handle the current number of tasks in // queue, but stop if queue becomes empty while doing so.
//k为核心数差值与队列大小最小数 int k = Math.min(delta, workQueue.size()); while (k-- > 0 && addWorker(null, true)) {
//一直创建新的线程,直到队列为空或者当前运行线程数==设置后的核心线程数 if (workQueue.isEmpty()) break; } } }
public void setMaximumPoolSize(int maximumPoolSize) {
//参数检查 if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException();
//覆盖 this.maximumPoolSize = maximumPoolSize;
//当前运行的线程数大于设置后的值,多余的线程中断 if (workerCountOf(ctl.get()) > maximumPoolSize) interruptIdleWorkers(); }
不过用这种方法设置的时候需要把Core和Max设置成一样的参数。
最后一个参数是拒绝策略:
默认的拒绝策略是AbortPolicy,即当工作线程数==最大线程数且队列满了,当有一个新的任务到来,就会拒绝,丢弃任务并抛出RejectedExecutionException异常(滚)
DiscardPolicy策略,和AbortPolicy区别在于,不会抛出异常(不理那种,比滚更狠)
DiscardOldestPolicy策略,丢弃队列最前面的任务,然后重新提交被拒绝的任务。(喜新厌旧)
CallerRunsPolicy策略,由调用该任务的线程去运行(别哭)
import java.util.Date; public class MyRunnable implements Runnable { private String command; public MyRunnable(String s) { this.command = s; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date()); processCommand(); System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date()); } private void processCommand() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return this.command; } }
public class ThreadPoolExecutorDemo { private static final int CORE_POOL_SIZE = 5; private static final int MAX_POOL_SIZE = 10; private static final int QUEUE_CAPACITY = 100; private static final Long KEEP_ALIVE_TIME = 1L; public static void main(String[] args) throws InterruptedException, ExecutionException { //使用阿里巴巴推荐的创建线程池的方式 //通过ThreadPoolExecutor构造函数自定义参数创建 ThreadPoolExecutor executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i < 10; i++) { //创建WorkerThread对象(WorkerThread类实现了Runnable 接口) Runnable worker = new MyRunnable("" + i); //执行Runnable:这里还有一种提交任务的方法submit,区别在于submit会返回Future对象 executor.execute(worker); } //终止线程池 executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("Finished all threads"); //重新设置核心线程数 executor.setCorePoolSize(9); System.out.println(executor.getCorePoolSize()); //重新设置最大线程数 executor.setMaximumPoolSize(20); System.out.println(executor.getMaximumPoolSize()); } }