温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

什么是线程池

发布时间:2021-06-29 11:28:35 来源:亿速云 阅读:89 作者:chen 栏目:大数据

这篇文章主要讲解了“什么是线程池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是线程池”吧!

1、线程池的核心类:ThreadPoolExecutor 

1.1-类图

(I:代表接口,C:代表实现类)I:Executor 
    I:ExecutorService
        C:AbstractExecutorService
            C:ThreadPoolExecutor

该类的主要构造函数如下:

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

1.2-参数详解:

  1. corePoolSize:核心线程池的大小。如果核心线程池有空闲位置,新的任务进来就会被核心线程池新建一个线程执行,执行完毕后不会销毁线程,线程会进入缓存队列等待再次被运行。

  2. maximunPoolSize:最大的线程数量。如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize,否侧会采取拒绝接受任务策略,我们下面会具体分析。

  3. keepAliveTime:非核心线程能够空闲的最长时间,超过时间,线程终止。这个参数默认只有在线程数量超过核心线程池大小时才会起作用。只要线程数量不超过核心线程大小,就不会起作用。

  4. unit:时间单位,和keepAliveTime配合使用。

  5. workQueue: 缓存队列,用来存放等待被执行的任务,有以下取值:
    1、ArrayBlockingQueue; 有界阻塞队列,详见文章:ArrayBlockingQueue详解
    2、LinkedBlockingQueue; 无界阻塞队列,详见文章:LinkedBlockingQueue详解
    3、SynchronousQueue; 无缓冲阻塞队列 ,详见文章:SynchronousQueue详解

  6. threadFactory:线程工厂,用来创建线程,默认new Executors.DefaultThreadFactory();

  7. handler: 线程拒绝策略。当创建的线程超出maximumPoolSize值,且缓冲队列已满时,对新提交任务的处理策略,有以下4种取值,我们结合代码分析
    1、ThreadPoolExecutor.AbortPolicy 无视任务(也就是丢弃任务),并通过抛异常告知调用者“我拒绝接收新任务”

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    	//这里直接抛出异常,可理解为:无视任务(也就是丢弃任务),并通过抛异常告知调用者“我拒绝接收新任务”
    	throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
    }

    2、ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不做任何处理。 

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        //这里是空实现,也就是丢弃任务,不作任何的处理
        //这会导致:虽然此策略被触发,但调用者根本不知道它提交进来的任务,最终到底有没有被执行
    }

    3、ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,尝试执行新任务。

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    	if (!e.isShutdown()) { //判断线程池是否被关闭了
    		e.getQueue().poll();//丢弃队列最前面的任务
    		e.execute(r); //尝试执行新任务
    	}
    }

    4、ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    	if (!e.isShutdown()) { //判断线程池是否被关闭了
    		r.run(); //调用线程直接调用run()方法,执行其代码逻辑
    	}
    }


     

1.3-Executors工厂类

    基于ThreadPoolExecutor 的构造参数如此之多,JDK为我们提供了Executors类,通过它我们可以简单的创建出四种类型的线程池,一般场景下够用了。
1.3.1、固定大小线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
                  nThreads, // corePoolSize
                  nThreads,// maximumPoolSize
                  0L, TimeUnit.MILLISECONDS,// keepAliveTime=0秒
                  new LinkedBlockingQueue<Runnable>()// 利用无界阻塞队列
               );
}

参数:corePoolSize = 固定值,maximumPoolSize = 固定值,keepAliveTime = 0秒 ,workQueue =  LinkedBlockingQueue 无界阻塞队列
分析:
1、corePoolSize 和 maximumPoolSize 都为 nThreads 一个固定值,说明此线程池中都是核心线程,keepAliveTime为核心线程空闲时间,该线程池中不存在非核心线程,所以参数keepAliveTime在此处无效
2、new LinkedBlockingQueue<Runnable>() :缓存队列用的无界阻塞队列,当核心池子里的线程都在忙的时候,新进来的任务被放到此队列中;一旦有空闲的线程了,该线程就会从队列里去拿任务执行;
3、缺点:因为用的是无界队列,所以当 nThreads 个线程一直被占用的情况下,同时又不断的有新任务进来,就有可能导致OOM问题;

1.3.2、单个线程的线程池

public static ExecutorService newSingleThreadExecutor() {
	return new FinalizableDelegatedExecutorService
		(new ThreadPoolExecutor(1, 1,  // corePoolSize,maximumPoolSize
								0L, TimeUnit.MILLISECONDS,// keepAliveTime=0秒
								new LinkedBlockingQueue<Runnable>())// 利用无界阻塞队列
                             );
}

参数:corePoolSize = 1,maximumPoolSize = 1,keepAliveTime = 0秒 ,workQueue =  LinkedBlockingQueue 无界阻塞队列
分析:
1、corePoolSize 和 maximumPoolSize 都为1,此线程池只有一个核心线程,同上,参数keepAliveTime在此处无效
2、new LinkedBlockingQueue<Runnable>() :缓存队列用的无界阻塞队列,当仅有的一个核心线程在忙的时候,新进来的任务被放到此队列中;待线程空闲了,就会从队列里去拿任务执行;
3、缺点:因为用的是无界队列,所以当核心线程一直被占用的情况下,同时又不断的有新任务进来,就有可能导致OOM问题;
4、有没有注意到,这里为什么用了一个FinalizableDelegatedExecutorService类呢?
见文章:关于newSingleThreadExecutor中的FinalizableDelegatedExecutorService源码分析

1.3.3、缓存线程池

public static ExecutorService newCachedThreadPool() {
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,// corePoolSize,maximumPoolSize
								  60L, TimeUnit.SECONDS,// keepAliveTime=60秒
								  new SynchronousQueue<Runnable>());// 同步阻塞队列
}

参数:corePoolSize = 0,maximumPoolSize = 无限大,keepAliveTime = 60秒(重要) ,workQueue =  SynchronousQueue 无缓冲阻塞队列
分析:一个可以根据需要创建线程的线程池,此线程池中所有线程都为非核心线程,且最大空闲时间为60秒,最多可以创建Integer.MAX_VALUE 个线程(2^31次方,21亿多,可以视为无限);SynchronousQueue 为无缓冲阻塞队列,也就是此队列里不会缓冲新的任务,有新任务进来时,如果无空闲线程,就会新创建一个线程;如果有空闲线程,就会使用空闲线程;所以此线程池适合执行一些执行周期短的任务。

感谢各位的阅读,以上就是“什么是线程池”的内容了,经过本文的学习后,相信大家对什么是线程池这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI