为什么要使用线程池?
如果使用线程池,频繁的创建和销毁线程会消耗大量系统资源,如果不对线程创建请求进行限制,在并发请求数量非常多,且线程执行时间很短的系统里,可能会造成系统的资源不足;同时,执行相同的任务会重复创建线程,无法重用任务。
使用线程池有什么优点?
框架类图:
Executor是Java语言提供的,用于管理任务的工具。它只有一个方法execute(Runnable command),作用是提交一个将要执行的任务,该命令可能在新的线程、已入池的线程或者正调用的线程等中执行,这取决于 Executor的实现。
一个简单的例子:
public class ExecutorTest implements Executor { //实现Executor接口
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
public class Main {
public static void main(String[] args) {
ExecutorTest executorTest=new ExecutorTest();
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("NewThread");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorTest.execute(runnable); //提交任务
}
}
ExecutorService继承并扩展了Executor接口,提供了停止接受任务(shutdown)、停止所有任务(shutdownNow)、有返回结果的任务(submit)等方法,可以更好地管理任务。
扩展了ExecutorService接口,添加了延时执行和定时执行的方法。
线程池有一下五种状态
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;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
核心线程池大小(corePoolSize)与最大线程池大小(maximumPoolSize)
ThreadPoolExecutor根据corePoolSize和maximumPoolSize设置的边界自动调整池大小。
保持存活时间(keepAliveTime)与其单位(unit)
如果当前线程池中工作线程多于corePoolSize,那么如果线程的空闲时间超过keepAliveTime,则超时的这部分线程将会终结。当然也可以使用setKeepAliveTime()进行动态修改。默认情况下,保持活动策略只在有多于 corePoolSizeThreads 的线程时应用。但是只要 keepAliveTime 值非 0,通过allowCoreThreadTimeOut() 方法也可将此超时策略应用于核心线程。
阻塞队列(BlockingQueue)
用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
阻塞队列的排队策略
拒绝执行处理程序(RejectedExecutionHandler)
由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
线程工厂(ThreadFactory)
执行程序创建新线程时使用的工厂类,一个最简单的实现:
public class SimpleThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
我们通常不直接使用ThreadPoolExecutor,而是用Executors类中提供的四种线程池:newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool、newScheduledThreadPool。
newFixedThreadPool
创建一个固定线程数的线程池,以无界队列方式来运行这些线程。
newSingleThreadExecutor
创建一个固定线程数为1的线程池,以无界队列方式来运行这些线程,运行顺序遵守线程优先级队列规则。
newCachedThreadPool
创建一个可根据需要创建新线程的线程池,在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
newScheduledThreadPool
创建一个线程池,它会按规定时间延迟执行或定期执行。
线程池中的线程被封装成为一个Worker对象,它继承自AQS并实现了Runnable接口。Worker使用AQS来实现不可重入的独占锁。lock方法一旦获取了独占锁,表示线程正在执行任务,所以不是独占锁的状态就代表线程空闲。
不可重入是因为不想让在调用比如setCorePoolSize()等线程池控制方法时可以再次获取锁,setCorePoolSize()时可能会interruptIdleWorkers(),在对一个线程interrupt时会要w.tryLock() ,如果可重入,就可能会在对线程池操作的方法中中断线程。