线程池就是首先创建一些线程,他们的集合称之为线程池。线程池在系统启动时会创建大量空闲线程,程序将一个任务传递给线程池,线程池就会启动一条线程来执行这个任务,执行结束后线程不会销毁(死亡),而是再次返回到线程池中成为空闲状态,等待执行下一个任务

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程

1. 为什么要使用线程池

多线程运行时,系统不断创建和销毁新的线程,成本非常高,会过度的消耗系统资源,从而可能导致系统资源崩溃,使用线程池就是最好的选择

2、创建线程池

public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           RejectedExecutionHandler handler)

  • corePoolSize:线程池核心线程数量
  • maximumPoolSize:线程池最大线程数量
  • keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
  • unit:存活时间的单位
  • workQueue:存放任务的队列
  • handler:超出线程范围和队列容量的任务的处理程序

示例:

通过Executor工厂类中的静态方法获取线程池对象

1、通过newCachedThreadPool获取线程池对象

该方式特点是:创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建

//1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        //2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }

2、通过newFixedThreadPool获取线程池对象

该方式特点是:可指定创建线程数,并且可以重复用

//1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newFixedThreadPool(3);
        //2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable2(i));
        }

3、通过newSingleThreadExecutor获取线程池对象

该方式特点是:只会创建一个线程

//1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor();
        //2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable3(i));
        }

小结

三种创建线程池的区别 第一种:newCachedThreadPool:线程的数据是不做限制的,每次有任务来的时候都会以任务优先,性能最大化(也就是服务器压力比较大)

第二种:newFixedThreadPool:可以让压力不那么大,并且可以规定线程的数量,当线程的数量达到指定数量的时候,这个时候就不会再有新的线程了

第三种:newSingleThreadExecutor:绝对的安全,不考虑性能,因为是单线程,永远只有一个线程来执行任务。

3. 线程池的工作流程

  • 核心线程数:5
  • 存放要执行任务的等待队列:10
  • 最大线程数:20
  • 拒绝策略:

工作过程:

1、提交任务,判断核心线程是否已满,如果未满,线程池调用addWorker(Runnable firstTask, boolean core)方法启动执行firstTask就是调用execute(Runnable command)方法中所传的那个command,core是该线程是否为核心线程,如果此时firstTask为null并且任务队列不为空则结束创建新的线程,让已有的核心线程处理,否者创建一个Worker对象执行任务,


private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{

    private static final long serialVersionUID = 6138294804551838833L;

    final Thread thread;

    Runnable firstTask;



        Worker(Runnable firstTask) { 
                runWorker this.firstTask = firstTask; this.thread =                 getThreadFactory().newThread(this); 
}
 
}

最后启动firtask任务

2、判断阻塞队列是否已满,如果未满,将任务放入队列中,等待执行,如果已满,进入下一步

3、如果线程池中的最大线程未满,创建线程执行任务,如果已满,按照拒绝策略进行处理。

注意:新建的非核心线程会先执行新进入的任务,并非等待队列中的任务。

4、当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
默认策略:ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务