zoukankan      html  css  js  c++  java
  • JAVA线程池

    线程池类

    Executor接口

    其中就只有一个函数

    void execute(Runnable command);
    

    接口是一种规范,那么这个接口,就是要求其实现类,能够处理Runable的实例,换句话说,就是管理线程

    ExecutorService

    public interface ExecutorService extends Executor {
        void shutdown();
        List<Runnable> shutdownNow();
        boolean isShutdown();
        boolean isTerminated();
        boolean awaitTermination(long timeout, TimeUnit unit)
                throws InterruptedException;
        <T> Future<T> submit(Callable<T> task);
        Future<?> submit(Runnable task);
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
                throws InterruptedException;
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                      long timeout, TimeUnit unit)
                throws InterruptedException;
        <T> T invokeAny(Collection<? extends Callable<T>> tasks)
                throws InterruptedException, ExecutionException;
        <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                        long timeout, TimeUnit unit)
                throws InterruptedException, ExecutionException, TimeoutException;
    }
    

    ExecutorService继承了Executor,也是与线程池关系相当密切的一个接口,它一共定义了11个方法 ,加上execute一共12个方法
    比起Execute,它不仅可以接收Runnable,还能接收Callable接口
    这些方法起什么作用等下结合其实现类分析

    Executors

    这个类中有一些静态的工程方法和内部类,用于生成线程池级相关类

    四个线程池实现类

    java中常用的线程池有四个

    CachedThreadPool
    FixedThreadPool
    ScheduledThreadPool
    SingleThreadExecutor
    

    通常,要创造他们的实例,都会使用Executors中的工厂方法

    ExecutorService executor1 = Executors.newCachedThreadPool();
    ExecutorService executor2 = Executors.newFixedThreadPool(8);
    ExecutorService executor3 = Executors.newScheduledThreadPool(5);
    ExecutorService executor4 = Executors.newSingleThreadExecutor();
    

    拿到他们的类名称看看,究竟他们的实例属于哪个类

    class java.util.concurrent.ThreadPoolExecutor
    class java.util.concurrent.ThreadPoolExecutor
    class java.util.concurrent.ScheduledThreadPoolExecutor
    class java.util.concurrent.Executors$FinalizableDelegatedExecutorService
    

    锁定目标之后,接下来就是逐个分析了

    线程池实现

    1.ThreadPoolExecutor

    继承树:

    上面的类信息表明,CachedThreadPool和FixedThreadPool实际上是属于同一个类,不同的应该只有创建时的参数不同而已
    为了描述的简洁,我们跳过中间的查找过程,直接看ThreadPoolExecutor的最重要的构造器

    七参构造器

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

    构造器的方法体里面就是把这些值赋给类属性
    可以看到这里一共7个参数,他们分别是:
    int corePoolSize:线程池最小(核心)的线程数,如果allowCoreThreadTimeOut被标志为true,那线程数也可能小于这个数
    注意这里的最小并不是要求线程数全程都大于等于corePoolSize,一开始是线程一个一个增加,到了corePoolSize这个数后,即使线程不用了,线程数也不会小于corePoolSize
    int maximumPoolSize:线程池最大线程数
    long keepAliveTime:线程数量大于corePoolSize时,多余线程的最大空闲存活时间,这个数字代表什么由下一个参数决定
    TimeUnit unit:上一个参数的单位,TimeUnit是一个枚举类,建议查看源码
    BlockingQueue workQueue:一个阻塞队列,用于存放线程
    ThreadFactory threadFactory:线程工厂,用于创建线程
    RejectedExecutionHandler handler:拒绝策略,用于线程池拒绝新的任务加入

    除了这个构造器外,ThreadPoolExecutor还有一个五个参数的构造器

    五(六)参构造器

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory //五参构造器没有这个参数
    )
    

    实际上,其内部也是调用了七个参数的构造器
    而后面的两个实参是:

    Executors.defaultThreadFactory()//五参构造器的默认值
    defaultHandler
    

    那么默认的defaultThreadFactory是什么样子的呢?

    DefaultThreadFactory

    Executors中的一个静态类,就是默认的线程工厂,除了构造器,之有一个方法:

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                namePrefix + threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
    

    简单来说,逻辑就是,这个线程不能是守护线程,默认的优先级是默认值5

    defaultHandler

    defaultHandler的实例是AbortPolicy这个类的实例
    它的拒绝策略是直接抛出异常

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                " rejected from " +
                e.toString());
    }
    

    相信大家早有耳闻,线程池一共四种拒绝策略:
    这里我们就见到了第一种

    FixedThreadPool的创建

    这两个都是Executors中的工厂方法

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

    FixedThreadPool:固定大小的线程池,其核心线程数量和最大核心数量都是nThreads,可以传入自定义的线程工厂,阻塞队列用的是LinkedBlockingQueue

    CachedThreadPool的创建

    这两个都是Executors中的工厂方法
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                threadFactory);
    }
    

    CachedThreadPool:可缓存线程池,核心线程数为0,最大线程数为int的最大值,最大线程空闲存活时间是60秒,可以传入自定义的线程工厂,,阻塞队列用的是SynchronousQueue

    FixedThreadPool和CachedThreadPool的区别

    这两种线程池甚至使用了相同的构造器,所以显然只有构造器参数上的差别
    数值的差别很好理解,但是为什么阻塞队列会有差别呢?

    瞎JB乱猜开始了!
    /**
    因为我们可以认为,CachedThreadPool可以同时运行的线程数实际上是无限的
    而阻塞队列存放的又是等待的任务
    对于CachedThreadPool来说,任务自然不需要等待,有任务来,就会立马有线程接手
    **/

    2.ScheduledThreadPoolExecutor

    定时的线程池

    这个线程池的构造器参数和ThreadPoolExecutor完全一样,这里就不多说了
    值得注意的是,默认的阻塞队列是DelayedWorkQueue,最大线程数是int最大值,而创建ScheduledThreadPoolExecutor需要指定的是corePoolSize,可选指定threadFactory
    这个类的具体实现肯定与上一个有所不同,但是本博客还没到阅读源码的水平,等下实践给出例子体会一下应该就差不多了

    3.FinalizableDelegatedExecutorService

    这是Executor的内部类,其实就是一个ThreadPoolExecutor的代理类,只开放了部分方法,比如invokeall就没有开放
    还重写了finalize方法,这是我以第一次见到有人这么干的

     protected void finalize() {
          super.shutdown();
     }
    

    工厂方法是这么写的

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

    4.其他线程池

    上网搜索一下,java线程池的种类,大部分博客都会告诉你是4种,就是上面的4位
    不知道这是那个版本jdk的说法了,实际上,在jdk8里面,Executors包含的工厂类就不止这4种.

    单线程的延迟线程池,它也是一个被Executor内部类代理的线程池,核心是ScheduledThreadPoolExecutor,核心线程数为1,最大线程池为int最大值,阻塞队列是LinkedBlockingQueue

    public static ScheduledExecutorService newSingleThreadScheduledExecutor();
    

    这个线程池我就不了解是什么东西了,它实际上是一个ForkJoinPool,这些以后再去了解吧

    public static ExecutorService newWorkStealingPool()
    

    现在的线程池一共6种了,然而有些线程池仅仅以为构建的方式不同就分为不同的线程池,这种分类不便于我们理解
    实际上,我们已知的线程池就是3种

    ThreadPoolExecutor
    ScheduledThreadPoolExecutor
    ForkJoinPool
    

    创建线程池不一定非要使用Executor,直接调用这些线程池的构造方法也可以
    如果你够强,完全可以写一个自定义的线程池呀

    补充

    我们刚刚提到过一些知识点,但是还没有看清全貌
    以下内容包含ThreadPoolExecutor和ScheduledThreadPoolExecutor,对于ForkJoinPool暂不分析

    1.拒绝策略

    拒绝策略就是线程池已经不能够接收新的任务时,对于新加入任务采取的措施

    AbortPolicy

    抛出异常

    CallerRunsPolicy

    由申请加入任务的线程自己执行

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
    

    DiscardPolicy

    直接丢弃,实际上就是什么都不做

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
    

    DiscardOldestPolicy

    丢弃最老的
    从这里看也不一定是最老的呀,这阻塞队列可不一定是先进先出的

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
    

    2.线程工厂

    DefaultThreadFactory

    默认的线程工厂,生成了一个非守护线程,优先级为5的线程

    PrivilegedThreadFactory

    这个类目前在我能力范围之外,大概与类加载器有关

    总结

    1.线程池种类

    一般分类:
    CachedThreadPool
    FixedThreadPool
    ScheduledThreadPool
    SingleThreadExecutor

    实际分类
    ThreadPoolExecutor
    ScheduledThreadPoolExecutor
    ForkJoinPool

    2.线程池参数(不包含ForkJoinPool)

    int corePoolSize
    int maximumPoolSize
    long keepAliveTime
    TimeUnit unit
    BlockingQueue workQueue
    ThreadFactory threadFactory
    RejectedExecutionHandler handler

    3.拒绝策略

    AbortPolicy:抛异常
    CallerRunsPolicy:交给原线程执行
    DiscardPolicy:直接丢弃
    DiscardOldestPolicy:丢弃最老的

    部分方法

    以下结论由测试得出,可能与事实有偏差

    invokeAny

    执行给定Callable集合中的某一个
    FixedThreadPool总是执行第一个
    CachedThreadPool大概率执行第一个
    ScheduledThreadPool总是执行第一个
    SingleThreadExecutor总是执行第一个

  • 相关阅读:
    20145331 《信息安全系统设计基础》实验一 开发环境的熟悉
    201453331 《信息安全系统设计基础》期中总结
    20145331 《信息安全系统设计基础》第7周学习总结
    20145331《信息安全系统设计基础》第六周学习总结
    20145331 《信息安全系统设计基础》第5周学习实践部分总结及代码分析
    20145331 《信息安全系统设计基础》第5周学习总结
    20145331 《信息安全系统设计基础》第3周学习总结
    20145331 《信息安全系统设计基础》第2周学习总结
    20145331 《信息安全系统设计基础》第1周学习总结
    20145330 《信息安全系统设计基础》第10周学习总结
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/12682868.html
Copyright © 2011-2022 走看看