java线程池

java线程池

1.java的线程池

线程池是一种多线程处理形式,处理过程中将任务添加队列,然后在创建线程后自动启动这些任务。

2.线程池的顶级接口

线程池的顶级接口是Executor,Executor并不是一个线程池,而只是一个执行线程的工具,而真正的线程池是ExecutorService。

3、使用线程池的优点

(1).重用线程池的线程,避免因为线程的创建和销毁锁带来的性能开销
(2).有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞。
(3).能够对线程进行简单的管理,并提供一下特定的操作如:可以提供定时、定期、单线程、并发数控制等功能

4、java中的有哪些线程池?
Java通过Executors提供四种线程池,分别为:

(1)、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
(2)、newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
(3)、newScheduledThreadPool创建一个定长线程池,支持定时和周期性任务执行
(4)、newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
5、线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

不管你调用的是ThreadPoolExecutor的哪个构造函数,最终都会执行到这个构造函数的,这个构造函数有7个参数,正是由于对这7个参数值的赋值不同,造成生成不同类型的线程池,比如我们常见的CachedThreadPoolExecutor、FixedThreadPoolExecutor
SingleThreadPoolExecutor、ScheduledThreadPoolExecutor,我们老看看这几个参数的具体含义:

    <1>corePoolSize:线程池中核心线程的数量;当提交一个任务到线程池的时候,线程池会创建一个线程来执行执行任务,即使有其他空闲的线程存在,直到线程数达到corePoolSize时不再创建,这时候会把提交的新任务放入到阻塞队列中,如果调用了线程池的preStartAllCoreThreads方法,则会在创建线程池的时候初始化出来核心线程;

    <2>maximumPoolSize:线程池允许创建的最大线程数;如果阻塞队列已经满了,同时已经创建的线程数小于最大线程数的话,那么会创建新的线程来处理阻塞队列中的任务;

    <3>keepAliveTime:线程活动保持时间,指的是工作线程空闲之后继续存活的时间,默认情况下,这个参数只有线程数大于corePoolSize的时候才会起作用,即当线程池中的线程数目大于corePoolSize的时候,如果某一个线程的空闲时间达到keepAliveTime,那么这个线程是会被终止的,直到线程池中的线程数目不大于corePoolSize;如果调用allowCoreThreadTimeOut的话,在线程池中线程数量不大于corePoolSize的时候,keepAliveTime参数也可以起作用的,知道线程数目为0为止;

    <4>unit:参数keepAliveTime的时间单位;

    <5>workQueue:阻塞队列;用于存储等待执行的任务,有四种阻塞队列类型,ArrayBlockingQueue(基于数组的有界阻塞队列)、LinkedBlockingQueue(基于链表结构的阻塞队列)、SynchronousQueue(不存储元素的阻塞队列)、PriorityBlockingQueue(具有优先级的阻塞队列);

    <6>threadFactory:用于创建线程的线程工厂;

    <7>handler:当阻塞队列满了,且没有空闲线程的情况下,也就是说这个时候,线程池中的线程数目已经达到了最大线程数量,处于饱和状态,那么必须采取一种策略来处理新提交的任务,我们可以自己定义处理策略,也可以使用系统已经提供给我们的策略,先来看看系统为我们提供的4种策略,AbortPolicy(直接抛出异常)、CallerRunsPolicy(只有调用者所在的线程来运行任务)、DiscardOldestPolicy(丢弃阻塞队列中最近的一个任务,并执行当前任务)、Discard(直接丢弃);

接下来就是将任务添加到线程池之后的运行流程了;

    我们可以调用submit或者execute方法,两者最大的区别在于,调用submit方法的话,我们可以传入一个实现Callable接口的对象,进而能在当前任务执行结束之后通过Future对象获得任务的返回值,submit内部实际上还是执行的execute方法;而调用execute方法的话,是不能获得任务执行结束之后的返回值的;此外,调用submit方法的话是可以抛出异常的,但是调用execute方法的话,异常在其内部得到了消化,也就是说异常在其内部得到了处理,不会向外传递的;

    因为submit方法最终也是会执行execute方法的,因此我们只需要了解execute方法就可以了:

    在execute方法内部会分三种情况来进行处理:

    <1>:首先判断当前线程池中的线程数量是否小于corePoolSize,如果小于的话,则直接通过addWorker方法创建一个新的Worker对象来执行我们当前的任务;

    <2>:如果说当前线程池中的线程数量大于corePoolSize的话,那么会尝试将当前任务添加到阻塞队列中,然后第二次检查线程池的状态,如果线程池不在Running状态的话,会将刚刚添加到阻塞队列中的任务移出,同时拒绝当前任务请求;如果第二次检查发现当前线程池处于Running状态的话,那么会查看当前线程池中的工作线程数量是否为0,如果为0的话,就会通过addWorker方法创建一个Worker对象出来处理阻塞队列中的任务;

    <3>:如果原先线程池就不处于Running状态或者我们刚刚将当前任务添加到阻塞队列的时候出现错误的话,那么会去尝试通过addWorker创建新的Worker来处理当前任务,如果添加失败的话,则拒绝当前任务请求;

6、线程池存在哪些状态:

查看ThreadPoolExecutor源码便知晓,存在5种状态:

// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;

    <1>Running:可以接受新任务,同时也可以处理阻塞队列里面的任务;

    <2>Shutdown:不可以接受新任务,但是可以处理阻塞队列里面的任务;

    <3>Stop:不可以接受新任务,也不处理阻塞队列里面的任务,同时还中断正在处理的任务;

    <4>Tidying:属于过渡阶段,在这个阶段表示所有的任务已经执行结束了,当前线程池中是不存在有效的线程的,并且将要调用terminated方法;

    <5>Terminated:终止状态,这个状态是在调用完terminated方法之后所处的状态;
    ,在调用shutdown方法的时候,线程池状态会从Running转换成Shutdown;在调用shutdownNow方法的时候,线程池状态会从Running/Shutdown转换成Stop;在阻塞队列为空同时线程池为空的情况下,线程池状态会从Shutdown转换成Tidying;在线程池为空的情况下,线程池状态会从Stop转换成Tidying;当调用terminated方法之后,线程池状态会从Tidying转换成Terminate;

7、线程池的实现原理
提交一个任务到线程池中,线程池的处理流程如下:

(1)、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

(2)、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。

(3)、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

8、线程池工作队列饱和策略
(1)Java线程池会将提交的任务先置于工作队列中,在从工作队列中获取(SynchronousQueue直接由生产者提交给工作线程)。那么工作队列就有两种实现策略:无界队列和有界队列。无界队列不存在饱和的问题,但是其问题是当请求持续高负载的话,任务会无脑的加入工作队列,那么很可能导致内存等资源溢出或者耗尽。而有界队列不会带来高负载导致的内存耗尽的问题,但是有引发工作队列已满情况下,新提交的任务如何管理的难题,这就是线程池工作队列饱和策略要解决的问题。

(2)饱和策略分为:Abort 策略, CallerRuns 策略,Discard策略,DiscardOlds策略。

1.Abort策略:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。

在主函数中添加如下代码:
policy(new ThreadPoolExecutor.AbortPolicy());

运行结果:程序抛出了RejectedExecutionException,并且一共运行了8个任务(线程池开始能运行3个任务,工作队列中存储5个队列)。当工作队列满了的时候,直接抛出了异常,而且JVM一直不退出

2.CallerRuns策略:为调节机制,既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。

在主函数运行:
policy((new ThreadPoolExecutor.CallerRunsPolicy()));
运行结果:所有的任务都被运行,且有2(10 - 3 -5)个任务是在main线程中执行成功的,8个任务在线程池中的线程执行的。

3.Discard策略:新提交的任务被抛弃。

在main函数中运行
policy(new ThreadPoolExecutor.DiscardPolicy());

运行结果:没有异常抛出,后面提交的2个新任务被抛弃,只处理了前8(3+5)个任务,JVM退出。

4.DiscardOldest策略:队列的是“队头”的任务,然后尝试提交新的任务。(不适合工作队列为优先队列场景)

在main函数中运行如下方法
policy(new ThreadPoolExecutor.DiscardOldestPolicy());

运行结果:一共运行8个任务,程序结束,后面添加的任务9,任务10被执行了,而前面的任务3,任务4被丢弃。

简言之:AbortPolicy());// 银行满了还有人进来,不处理,抛出异常(默认)
CallerRunsPolicy();// 银行满了,不处理,哪里来的去哪里,一般抛给main线程
DiscardPolicy();// 银行满了,把该线程丢掉,不抛异常
DiscardOldestPolicy();// 银行满了,会和先来的线程竞争,不抛异常

9、详细介绍四种线程池

(1)、newCachedThreadPool:

ExecutorService mCachelThreadPool = Executors.newCachedThreadPool();
创建一个可缓存线程池程>是一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE,这个数是很大的,一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。
调用execute将重用以前构造的线程(如果线程可用)。
比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。

(2)、newFixedThreadPool :

ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(5);

创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程 处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求

(3)、newScheduledThreadPool

  //设置池中核心数量是2
    ScheduledExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(2); 

创建一个线程池,它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。

(4)、newSingleThreadExecutor

ExecutorService mSingleThreadPool = Executors.newSingleThreadExecutor();

这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值