JAVA多线程-线程池基础

线程池

一、线程池作用?

1、提高效率:线程的创建和销毁对于系统资源的消耗是比较大的,线程池创建好一定数量的线程,来任务的时候,从线程池创建好的线程获取线程,省去了创建和销毁线程的过程

2、方便管理:编写线程池管理代码对线程池中的线程进行统一管理,比如缓存队列,任务到达线程数上限时,加入缓存队列排队等候,避免无限制的创建线程导致系统崩溃,

二、创建线程方式(4种)及使用场景?

1、newSingleThreadExecutor
说明: 创建一个单线程的线程池
队列: 内部使用LinkedBlockingQueue作为阻塞队列
特点: 如果该线程异常结束,会重新创建一个新的线程继续执行任务,用唯一的工作线程保证所有任务按照指定顺序执行(FIFO,LIFO,优先级)

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

2、newFixedThreadPool
说明: 创建一个定长线程池,可控制最大线程最大并发数,corePoolSize=maxiMumPoolSize,
队列: 内部使用LinkedBlockingQueue作为阻塞队列
特点: 线程数量到达核心线程数量时,进来的任务不会触发创建线程,在队列排队等待获取线程(FIFO,LIFO,优先级),线程池没有可执行任务时,也不会释放线程。

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

3、newCachedThreadPool
说明: 创建一个可缓存线程池,默认缓存 60s,线程池的线程数可达到 Integer.MAX_VA LUE,即 2147483647
队列: 内部使用SynchronousQueue作为阻塞队列
特点: 在没有任务执行时,当线程的空闲时间超过 keepAliveTime,会自动释放线程资源;当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销; 因此,使用时要注意控制并发的任务数,防止因创建大量的线程导致而降低性能

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

4、newScheduledThreadPool
创建一个定时线程池,支持定时和周期性的执行任务
说明: 创建一个定时线程池,定时和周期性的执行任务
特点: 在实际的业务场景中可以使 用该线程池定期的同步数据

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

总结: 除了 newScheduledThreadPool 的内部实现特殊一点之外,其它线程池内部都是基于 Thread PoolExecutor 类(Executor 的子类)实现的。

三、工作队列

1、ArrayblockingQueue
一个基于数组结构的有界阻塞队列,队列以FIFO对元素进行排序
2、LinkBlockingQueue
一个基于链表结构的阻塞队列,吞吐量高于ArrayBlockingQueue,静态工厂方法Executors.NewFixedThreadPool使用了这个队列
3、SychronousQueue
一个不储存元素的阻塞队列,每个插入操作都必须等到另一个线程调用移除操作,如果没有,插入操作一直处于阻塞状态,吞吐量大于LinkBlockingQueue,Executors.newCacheThreadPool使用了此队列
4、PriorityBlockingQueue
一个具有优先级的无界阻塞队列

四、无界队列和有界队列

有界队列
1.初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行 。
2.当提交的任务数超过了corePoolSize,会将当前的runable提交到一个block queue中。
3.有界队列满了之后,如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务。
4.如果3中也无法处理了,就会走到第四步执行reject操作。

无界队列
与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加,若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

五、7种重要的参数及流程说明

corePoolSize             //核心线程数量 ;
maximumPoolSize          //最大线程数量 ;
keepAliveTime            //线程保持时间,N个时间单位;
unit                     //时间单位(比如秒,分);
workQueue                //阻塞队列 ;
threadFactory            //线程工厂;
handler                  //线程池拒绝策略;

1、corePoolSize
核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
2、maximumPoolSize
线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
3、keepAliveTime
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
4、unit
参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

5、workQueue
一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择

ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和SynchronousQueue。线程池的排队策略与BlockingQueue有关
6、ThreadFactory
用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等

7、handler
表示当拒绝处理任务时的策略,有以下四种取值

1AbortPolicy           //直接抛出异常。
2CallerRunsPolicy      //只用调用者所在线程来运行任务。
3DiscardOldestPolicy   //丢弃队列里最近的一个任务,并执行当前任务。
4DiscardPolicy         //不处理,丢弃掉。
5、实现RejectedExecutionHandler接口 //根据应用场景需要自定义策略。如记录日志或持久化不能处理的任务。
   /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

    /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

六、线程池状态

  • RUNNING

自然是运行状态,指可以接受任务执行队列里的任务

  • SHUTDOWN

指调用了 shutdown() 方法,不再接受新任务了,但是队列里的任务得执行完毕。

  • STOP

指调用了 shutdownNow() 方法,不再接受新任务,同时抛弃阻塞队列里的所有任务并中断所 有正在执行任务。

  • TIDYING

所有任务都执行完毕,在调用 shutdown()/shutdownNow() 中都会尝试更新为这个状态。

  • TERMINATED

终止状态,当执行 terminated() 后会更新为这个状态

submit 和 execute 区别
  • execute 没有返回值,如果不需要知道线程的结果就使用execute 方法,性能较submit好
  • ubmit 返回一个 Future 对象,如果想知道线程结果就使用submit 提交,而且它能在主线程中通过 Future 的 get 方法捕获线程中的异常
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值