诸如 Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务。请求以某种方式到达服务器,这种方式可能是通过网络协议(例如 HTTP、FTP )、通过 JMS队列或者可能通过轮询数据库。不管请求如何到达,服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。每当一个请求到达就创建一个新线程,然后在新线程中为请求服务,但是频繁的创建线程,销毁线程所带来的系统开销其实是非常大的。
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
风险与机遇:
用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,
诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足和线程泄漏。
Java通过Executors提供四种线程池
线程代码
public class ThreadForPools implements Runnable {
private Integer index;
public ThreadForPools(Integer index) {
this.index = index;
}
@Override
public void run() {
try {
System.out.println("开始处理线程");
Thread.sleep(index*100);
System.out.println("我的线程标识是"+this.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
newCachedThreadPool
可以有无限多的线程进来(线程地址不一样),但是需要注意机器的性能。
/**
* 创建可缓存的线程池
*/
public class MyCachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
cachedThreadPool.execute(new ThreadForPools(i));
}
}
}

public class MyFixedThreadPool {
public static void main(String[] args) {
//线程池允许同时存在两个线程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
fixedThreadPool.execute(new ThreadForPools(i));
}
}
}

//schedule(commod,delay,unit)这个方法是说明系统启动后,需要等待多久时间执行,delay是等待的时间,只执行一次没有周期性。
public class MyScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 5; i++) {
scheduledThreadPool.schedule(new ThreadForPools(i),5,TimeUnit.SECONDS);
}
}
}

//scheduleAtFixedRate(commod,initialDelay,period,unit),这个是以period周期性执行任务,initialDelay是系统启动等待时间。
public class MyScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++) {
//线程启动等待2秒执行,之后每3秒执行一个周期
scheduledThreadPool.scheduleAtFixedRate(new ThreadForPools(i),2,3,TimeUnit.SECONDS);
}
}
}

//scheduleWithFixedDelay(commod,initialDelay,period,unit),这个是以period周期性执行任务,initialDelay是系统启动等待时间,和scheduleAtFixedRate的区别在于系统等待的时间不记在周期性执行的时间内。
public class MyScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++) {
//线程启动等待2秒执行,之后每3秒执行一个周期
scheduledThreadPool.scheduleWithFixedDelay(new ThreadForPools(i),5,3,TimeUnit.SECONDS);
}
}
}

public class MySingleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
singleThreadExecutor.execute(new ThreadForPools(i));
}
}
}

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);
...
}

Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;然后ThreadPoolExecutor继承了类AbstractExecutorService。
(1)线程池的状态
volatile int runState;
static final int RUNNING = 0;
static final int SHUTDOWN = 1;
static final int stop = 2;
static final int TERMINATED = 3;
runState表示当前线程池的状态,用volatile变量用来保证线程之间的可见性。
当创建线程池后,初始化时为RUNNING状态。
如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时它不能够接受新的任务,它会等所有的任务执行完毕。
如果调用了shutdownNow()方法,则线程池处于SHOP状态,此时线程池不能接受新的任务,并且去尝试终止正在运行的任务。
当线程池处于SHUTDOWN或者STOP状态,并且所有工作线程已经很销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATEDzhua状态

(2)任务的执行
private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来存放等待执行的任务
private final ReetrantLock mainLock = new ReetrantLock(); //线程池的主要锁状态,对线程池的状态改变的核心锁
private final HashSet<Worker> workers = new HashSet<Worker>(); //用来存放工作集
private volatile long keepAliveTime; //线程存活时间
private volatile boolean allowCoreThreadTimeOut; //是否允许为核心线程设置存活时间
private volatile int corePoolSize; //核心池的大小(即线程池中线程数目大于这个参数时,提交的任务会放在任务缓存队列)
private volatile int maximumPoolSize; //线程池中最大能容忍的线程数
private volatile int poolSize; //线程池中当前线程数
private volatile RejectedExecutionHandler handler; //任务拒绝策略
private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程
private int largestPoolSize; //用来记录线程池中曾经出现的最大线程数
private long completedTaskCount; //用来记录已经执行完毕的任务个数
public void execute(Runnable command) {
//首先先判断传入的任务是否为空,若是null,则抛出空指针异常;
if (command == null)
throw new NullPointerException();
//如果当前线程数量不小于核心池的数量或者执行addIfUnderCorePoolSize()方法,返回false主席那个下面代码块
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
//判断线程池状态是否为RUNNING,并且放入缓存队列
if (runState == RUNNING && workQueue.offer(command)) {
//这句话是为了防止在将此任务添加进任务缓存队列的同时其他线程突然调用了shutdown或者shutdownNow方法时,那就调用
//ensureQueuedTaskHandled方法
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
//如果不是RUNNING状态,并且调用addIfUnderMaximumPoolSize方法失败,则执行拒绝处理。
else if (!addIfUnderMaximumPoolSize(command))
//拒绝处理
reject(command); // is shutdown or saturated
}
}
(3)线程池中的线程初始化
默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务后才会创建线程。
在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个办法办到:
public boolean prestartCoreThread(){
return addIfUnderCorePoolSize(null); //注意传进去的参数是null
}
public int prestartAllCoreThreads(){
int n = 0;
while(addIfUnderCorePoolSize(null)) //注意传进去的参数是null
++n;
return n;
}
(4)任务缓存队列以及排队策略
(5)任务拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
(6)线程池的关闭
ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:
(7)线程池容量的动态调整
ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize()
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//创建线程池,核心池5个,最大的线程池数量10个,多余线程空闲存活时间,任务缓存对列及排队策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(5));
for (int i = 0; i < 15; i++) {
//创建15个任务
MyTask myTask = new MyTask(i);
//每创建一个放在线程池中
executor.execute(myTask);
/**
* getPoolSize():获取线程池中线程数目
* getQueue().size():获取队列中等待的任务数目
* getCompletedTaskCount():获取已经执行完成的任务数目
*/
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
executor.getQueue().size() + ",已执行完的任务数目:" + executor.getCompletedTaskCount());
}
//结束线程池生命周期
executor.shutdown();
}
}
class MyTask implements Runnable {
private int taskNum;
public MyTask(int taskNum) {
this.taskNum = taskNum;
}
@Override
public void run() {
System.out.println("正在执行task:" + taskNum);
try {
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task:" + taskNum + "执行完毕");
}
}

