浅谈线程池

线程池优点

  1. 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  2. 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  4. 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池核心设计与实现

线程池的总体设计

  • Executor是顶层接口,提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。
  • ExecutorService接口增加了一些能力:
       1.扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;
       2.提供了管控线程池的方法,比如停止线程池的运行。
  • AbstractExecutorService是ThreadPoolExecutor的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。
  • Java中的线程池核心实现类是ThreadPoolExecutor,ThreadPoolExecutor 将会一方面维护自身的生命周期,同时管理线程和任务,使两者良好的结合从而执行并行任务。

ThreadPoolExecutor的运行

线程池的运行主要分成两部分:任务管理、线程管理(内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦)。
任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:
(1)直接申请线程执行该任务;
(2)缓冲到队列中等待线程执行;
(3)拒绝该任务。

生命周期管理

线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。
线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)。
在具体实现中,线程池将运行状态(runState)、线程数量 (workerCount)两个关键参数的维护放在了一起。

ThreadPoolExecutor 运行的5种状态

ThreadPoolExecutor 运行的5种状态
Running能接受新提交的任务,并且也能处理阻塞队列里的任务。
Shutdown关闭状态,不再接受新提交的任务,但可以继续处理阻塞队列里的任务。
Stop不能接受新任务,也不能处理队列里的任务,会中断正在处理任务的线程。
Tidying所有任务都终止,workerCount(有效线程数)为0.
Terminated在terminated()方法执行后进入此状态。

ThreadPoolExecutor的生命周期转换

任务执行机制

任务调度

首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
1.如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
2.如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
3.如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
4.如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

执行流程

任务缓冲

阻塞队列(BlockingQueue),在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
不同的队列可以实现不一样的任务存取策略

阻塞队列(BlockingQueue)
ArrayBlockingQueue数组实现的有界阻塞队列,按照FIFO原则对元素排序,支持公平锁和非公平锁。
LikedBlockingQueue链表结构组成的有界队列,按照FIFO原则对元素排序,默认长度为Integer.MAX_VALUE。
PrioriBlockQueue支持线程优先级排序的无界队列,默认自然排序,也可实现compareTo()方法排序。
DelayQueue延时获取的无界队列,创建元素时可指定多久后才可以从队列中获取元素。
SynchronousQueue不储存元素,每次put必须等待take,否则不能添加元素,支持公平锁和非公平锁。
LinkedTransferQueue链表构成的无界队列,多了transfer()、tryTransfer()在队尾插元素方法。
LinkedBlockingDeque链表构成的双向队列,头尾都可添加移除元素,可降低锁的竞争。

任务申请

(1)一种是任务直接由新创建的线程执行。
(2)是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行,线程从任务缓存中不断地取任务执行。这部分策略由getTask方法实现。

任务拒绝

四种拒绝策略

拒绝策略
ThreadPoolExecutor.AbortPolicy线程池默认1拒绝策略,丢弃任务并抛出RejectedExecutionException异常(推荐使用)。
ThreadPoolExecutor.DiscardPolicy丢弃任务,不抛异常。
ThreadPoolExecutor.DiscardOldestPolicy丢弃队列前面的任务,重新提交被拒绝的任务。
ThreadPoolExecutor.CallerRunnsPolicy由调用线程(提交任务的线程)处理该任务,让每个任务都执行完毕。

ThreadPoolExecutor

ThreadPoolExecutor是创建线程池最传统和最推荐使用的方式,创建时要设置线程池的核心线程数和最大线程数还有任务队列集合。

线程池参数

线程池参数
corePoolSize线程池核心线程数,没有任务时,核心线程也会存活。设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。
maximumPoolSize线程池最大数。当线程数>= corePoolSize ,且任务队列已满时。线程池会创建新线程来处理任务,当线程数= maximumPoolSize ,且任务队列已满时,线程池会拒绝处理任务而抛出异常。
keepAliveTime空闲线程存活时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize。如果 allowCoreThreadTimeout=true,则会直到线程数量=0。
unit时间单位。
workQueue线程池所使用的缓冲队列,当核心线程数达到最大时,新任务会放在队列中排队等待执行。
handler线程池对拒绝任务的处理策略。
ThreadFactory负责生产线程的线程工厂。是一个接口类,而且方法只有一个。就是创建一个线程。为了统一在创建线程时设置一些参数,是否守护线程,优先级等。通过这个 TreadFactory 创建出来的线程能保证有相同的特性。

ThreadPoolExecutor的一些方法

ThreadPoolExecutor的一些方法
submit()/execute()执行线程池
shutdown()/shutdownNow()终止线程池
isShutdown()判断线程是否终止
getActiveCount()正在运行的线程数
getCorePoolSize()获取核心线程数
getMaximumPoolSize()获取最大线程数
getQueue()获取线程池中的任务队列
allowCoreThreadTimeOut(boolean)设置空闲时是否回收核心线程                                                                     

Executors创建线程池

Executors下几个常见线程池
FixedThreadPool(n)创建一个数量固定的线程池,超出的任务会在队列中等待空闲的线程,可用于控制程序的最大并发数。
CachedThreadPool()短时间内处理大量工作的线程池,会根据任务数量产生对应的线程,并试图缓存线程以便重复使用,如果限制 60 秒没被使用,则会被移除缓存。
SingleThreadExecutor()创建一个单线程线程池。
ScheduledThreadPool(n)创建一个数量固定的线程池,支持执行定时性或周期性任务。
SingleThreadScheduledExecutor()此线程池就是单线程的 newScheduledThreadPool。
WorkStealingPool(n)Java 8 新增创建线程池的方法,创建时如果不设置任何参数,则以当前机器处理器个数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。

线程池创建Demo

public class Test002 {

    private static int corePoolSize =10;
    private static int maxmumPoolSize =30;
    private static long keepTime = 30;
    private static TimeUnit unit = TimeUnit.SECONDS;
    private static ArrayBlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(1000);
    private static ThreadFactory threadFactory1 =  Executors.defaultThreadFactory();
    private static ThreadFactory threadFactory2 = r -> {
                                                          Thread t = new Thread(r);
                                                          return t;
    };
    private static ThreadPoolExecutor.AbortPolicy policy = new ThreadPoolExecutor.AbortPolicy();

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = new ThreadPoolExecutor(
                corePoolSize,
                maxmumPoolSize,
                keepTime,
                unit,
                blockingQueue,
                threadFactory1,
                policy);
        executorService.execute(new Runnable(){
            public void run(){
                System.out.println("new Runnable!");
            };
        });

        executorService.execute(()->{
            System.out.println("Lambada");
        });

         /*
          //线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行。
          executorService.shutdownNow();
         */
         /*
          //线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。
          executorService.shutdown();
         */

        // 这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,
        // 否则返回false,一般情况下会和shutdown方法组合使用。
        boolean boole = executorService.awaitTermination(3,TimeUnit.SECONDS);

    }

}

ScheduledThreadPoolExecutor(定时任务线程池)

public class ScheduleExectorsService_Demo {
    public static void main(String[]args)throws InterruptedException,ExecutionException{
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);
		
        //延迟两秒执行任务
        ScheduledFuture<?> future1 =executor.schedule(()->System.out.println("延迟2秒后开始工作的线程"),2, TimeUnit.SECONDS);
        //取消任务
        TimeUnit.SECONDS.sleep(4);
        System.out.println("都4秒了,不玩了"+Thread.currentThread().getName()+future1.cancel(true));
		
		//延时6秒开始跑,上一次任务开始后, 每隔1秒执行一次任务。
        ScheduledFuture<?> future2 =executor.scheduleAtFixedRate(()->System.out.println("开跑了1"),6,1, TimeUnit.SECONDS);
        //延时6秒开始跑, 上一次任务完成后, 延迟2秒启动任务。
        ScheduledFuture<?> future3 =executor.scheduleWithFixedDelay(()->System.out.println("开跑了2"),6,2, TimeUnit.SECONDS);
		
		/* 
		打断线程
		boolean getContinueExistingPeriodicTasksAfterShutdownPolicy():
		取有关在此执行程序已 shutdown 的情况下、是否继续执行现有定期任务的策略。
		boolean getExecuteExistingDelayedTasksAfterShutdownPolicy():
		获取有关在此执行程序已 shutdown 的情况下是否继续执行现有延迟任务的策略
		*/
	    // true:让正在执行的线程执行完; false:不让当前正在跑的线程顺利结束。
        executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
	    executor.shutdown();
    }
}

Future(submit执行Callable)

public class FutureExample_Demo1 {
        public static void main(String [] args)throws  Exception{
        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
        //根据8的推导特性,传入Callable。

        Future<Map> future = executorService.submit(()->{
            Map<String,String> map = new HashMap<>();
            map.put("111","111111");
            return map;
        });
        //直接拿。
        Map result = future.get();
        //延时拿(拿不到抛TimeoutException)。
        result = future.get(2, TimeUnit.SECONDS);
        System.out.println("从Future<Integer>中拿出结果"+result);
        //判断线程是否结束。
        boolean boole = future.isDone();
		// 设 true 会打断,设 false 会接着跑。
        boolean boole1 = future.cancel(false);
		boolean boole2 = future.isCancelled();	 
        executorService.shutdown();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值