并发与高并发课程学习笔记(10)

线程池

首先是不推荐使用传统的new Thread,来创建线程。

1.每次new Thread新建对象,性能差。

2.线程缺乏统一的管理,可能无限制的新建线程,相互竞争,有可能占用过多的系统资源导致死机或oom。

3.缺少更多的功能,如更多执行,定期执行,线程中断

关于使用线程池的好处

1.重用存在的线程,减少对象创建,消亡的开销,性能佳

2.可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多的资源竞争,避免阻塞。

3.提供定时执行,定期执行,单线程,并发数控制等功能

Executor

Executor是一个接口,跟线程池有关的基本都要跟他打交道,Executor接口很简单,只有一个execute方法。

ExecutorService是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。

AbstractExecutorService是一个抽象类。ThreadPoolExecutor就是实现了这个类。

ThreadPoolExecutor

常用的构造方法:

ThreadPoolExecutor(int corePoolSize,

                        int maximumPoolSize,

                        long keepAliveTime,

                        TimeUnit unit,

                        BlockingQueue workQueue)

ThreadPoolExecutor(int corePoolSize,

                        int maximumPoolSize,

                        long keepAliveTime,

                        TimeUnit unit,

                        BlockingQueue workQueue,

                        ThreadFactory threadFactory)

ThreadPoolExecutor(int corePoolSize,

                        int maximumPoolSize,

                        long keepAliveTime,

                        TimeUnit unit,

                        BlockingQueue workQueue,

                        RejectedExecutionHandler handler)

ThreadPoolExecutor(int corePoolSize,

                        int maximumPoolSize,

                        long keepAliveTime,

                        TimeUnit unit,

                        BlockingQueue workQueue,

                        ThreadFactory threadFactory,

                        RejectedExecutionHandler handler)

corePoolSize

核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。

maximumPoolSize

线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。

keepAliveTime

非核心线程的闲置超时时间,超过这个时间就会被回收。

unit

指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。

workQueue

线程池中的任务队列.

常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。

threadFactory

线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

public interfaceThreadFactory{

        Thread newThread(Runnable r); //通过线程工厂可以对线程的一些属性进行定制。

}

RejectedExecutionHandler

RejectedExecutionHandler也是一个接口,只有一个方法

public interfaceRejectedExecutionHandler{ 

     void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);

}

当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。


线程池规则

线程池的线程执行规则跟任务队列有很大的关系。

下面都假设任务队列没有大小限制:

    1.如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中。

    2.如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingDeque的时候,超过核心线程数量的任务会放在任务队列中排队。

    3.如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除。

    4.如果线程数量>核心线程数,并且>最大线程数,当任务队列是LinkedBlockingDeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数。

    5.如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常。

任务队列大小有限时:

    1.当LinkedBlockingDeque塞满时,新增的任务会直接创建新线程来执行,当创建的线程数量超过最大线程数量时会抛异常。

    2.SynchronousQueue没有数量限制。因为他根本不保持这些任务,而是直接交给线程池去执行。当任务数量超过最大线程数时会直接抛异常。

线程池的状态


并发与高并发课程学习笔记(10)_第1张图片

RUNNING:在RUNNING状态下线程池可以处理新提交的任务,也可以处理请求队列中的任务

SHUTDOWN:在该状态下线程池不会接收新提交的任务,但是可以继续处理阻塞队列中的任务,由线程池调用shutdown()方法转换

STOP:在该状态下线程池不会接收新提交的任务,也不会继续处理阻塞队列中任务,正在执行中的任务也会中断,由RUNNING状态调用shutdownNow()方法转换

TIDYING:在SHUTDOWN状态下阻塞队列为空或STOP状态下线程池中工作线程为0时转换4

TERMINATED:由TIDYING状态下自动转换,不需关心

线程池提供的常用方法

execute():提交任务,交给线程池执行

submit():提交任务,能够返回执行结果 相当于execute+Future

shutdown():关闭线程池,等待任务都执行完毕

shutdownNow():关闭线程池,不等待任务执行完毕

getTaskCount():线程池已执行和未执行的任务总数

getCompeletedTaskCount():已完成的任务数量

getPoolSize():线程池当前的线程数量

getActiveCount():当前线程池中正在执行任务的线程数量

线程池-Executors框架接口

Executors.newCachedThreadPool   //创建一个可缓存的线程池,如果线程池超过了可选择的需要,可以灵活回收空闲线程,如果没有可回收的则创建新的线程

Executors.newFixedThreadPool  //创建一个定长线程池,可以控制最大并发数,超出的线程会在队列中等待

Executors.newScheduledThreadPool  //创建一个定长线程池,它支持定时,以及周期的任务执行

Executors.newSingleThreadExecutor  //创建一个单线程的线程池,它只会用唯一的一个工作线程,保证所有任务按指定顺序执行

例子:

ExecutorService executorService = Executors.newCachedThreadPool();  //初始化线程池

executorService.execute(new Runnable() {    //线程执行方法1

        @Override

        public void run(){

            log.info("task:{}",index);

        }

});

executorService.execute(new Thread());  //线程执行方法2

executorService.shutDown();  //使用线程池后一定要释放线程池,切记

你可能感兴趣的:(并发与高并发课程学习笔记(10))