线程池 Executor 一篇简单笔记就懂

线程池Executor

目录

1、线程池简介

2、线程池的好处

3、线程池执行任务的规则

4、线程池的分类

1)FixedThreadPool类型

2)CachedThreadPool类型

3)ScheduleThreadPool类型

4)SingleThreadExecutor类型


1、线程池简介

线程池是在java开发中很重要的一个概念,Android中的线程池和和java是保持一致的,并无什么区别。线程池是一个抽象的概念,在java中是一个接口类,用Executor表示, 具体实现类为ThreadPoolExecutor,它们位于java.util.concurrent包下面,这个接口类很短,而且接口声明就只有一个,并且注释中说明了一些情况怎么使用,

package java.util.concurrent;
public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

上面省略了注释,可以看注释,很管用,上面有模版,教我们怎么创建一个自己线程池,其中有段是这么说的:许多线程池的实现类对将要执行的任务的方式和时间做了限制,下面展示了串型线程池在执行任务的时候,从一个任务到下一个任务的过程。

下面代码来至注释中的举例,说明了一个从任务如何进入到下一个任务的串行执行过程:

package demo.xx.patten.xx;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;

public class SerialExecutor implements Executor {
    final Queue<Runnable> tasks = new ArrayDeque<>();
    final Executor executor;
    Runnable active;

    public SerialExecutor(Executor executor) {
        this.executor = executor;
    }

    public synchronized void execute(final Runnable r) {
        tasks.add(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (active == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((active = tasks.poll()) != null) {
            executor.execute(active);
        }
    }
    
}

上面的SerialExecutor管理了一个Runnable对象的任务列队tasks和一个从构造函数中传入的线程池,每次调用SerialExecutor的execute方法的时候都会传入一个Runnable对象,然后将这个runnbale的执行方法run方法包装进一个新建的Runnable对象中,然后将这个新建的Runnbale对象插入tasks队列中。紧接着由于初始化的时候active是null,于是会首先执行scheduleNext()方法,这个时候通过tasks.poll()方法将插入的任务,也就是Runnable对象取出来交给构造函数中传入的Executor对象实例来执行,注意上面try的finally块中在r.run()被执行后都会被调用,这就保证了从一个任务到下一个任务的传递,因为active在第一执行scheduleNext后会被负值,之后就不等于null了,那么判断语句就不会被执行了。

 

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        
    }
  • corePollSize: 线程池的核心线程数的最大数量,默认情况下,核心线程会在在线程中一直存活,即使处于闲置状态。但如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则核心线程数在闲置一段时间后,会被回收,这个事件由keepAliveTime指定;
  • maxinumPoolSize: 线程池锁容纳的最大线程数,当活动线程达到这个数后,新来的任务将被阻塞;
  • keepAliveTime 非核心线程闲置的最大时长,超过后就会被回收,当allowCoreThreadTimeOut属性设置为true后,闲置的核心线程超过这个事件也会被回收;
  • unit 用于指定keepAliveTime参数的事件单位,这是一个枚举类型,常用的有TimeUnit.MILLISECONDS, TimeUnit.SECONDS, TimeUnit.MINUTES等;
  • workQueue 线程池的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个队列中;
  • threadFactory 线程工厂, 为线程池提供创建新线程的功能,ThreadFactory只是一个接口,也只有一个方法: Thread newThread(Runnable r);

 

2、线程池的好处

  1. 线程复用,避免现线程创建和销毁带来的的性能开销;
  2. 有效控制线程池的最大并发数,避免大量线程之间抢夺资源造成阻塞;
  3. 能够对线程进行简单的管理,并提供定时执行及指定间隔循环等功能;

 

3、线程池执行任务的规则

  1. 如果线程池中线程数量未达到核心线程的数量,会直接启动一个核心线程去执行任务;
  2. 如果线程池中线程的数量已经达到或者超过了核心线程的数量,那么接下来的任务会被插入到任务列队(workQueue)中排队等待执行;
  3. 如果workQueue中线程数量达到了列队的上线,导致任务无法再插入任务列队,并且线程池中的线程的数量没有达到线程池设定的线程数上限,这种情况下会启动非核心线程来执行任务;
  4. 如果步骤3中线程数量达到线程池规定最大值(maxmumPoolSize),那么线程池会拒绝执行新添加的任务,并且ThreadPoolExecutor会调用RejectedExecutionHandler的的rejectedExecution方法来通知调用者。

 

4、线程池的分类

从前面的介绍我们了解到,要直接创建一个线程池,需要传入很多个参数,这在实际使用中,就会显得比较麻烦,所以在Executors这个线程池工具类中,给我们提供了更加简单的创建线程池的API, 主要有四种类型,注意仅仅是类型,并不是存在的java类

 

1FixedThreadPool类型

通过Executors.newFixedThreadPool(int nThreads)创建

 创建一个固定线程数量的线程池,线程池中的所有线程都是核心线程,当线程空闲时不会被回收,也不会超时,除非线程池被关闭,当线程池中线程都处于活动状态的时候,新进任务会处于等待状态,直到有线程空出来去处理,处于等待状态的任务会在任务列队中排队等候,这个列队的容量没有大小限制,由于线程不会被回收,这样便能够快速的响应外界请求。

 

2CachedThreadPool类型

通过Executors.newCachedThreadPool()创建

这是一个线程池的线程的数量是不定的,所有线程都是非核心线程,最大线程数量为Integer.MAX_VALUE,当线程池中线程都处于活动状态的时候,线程池会创建新的线程池来处理新进任务,否则就会利用空闲线程来处理新进任务,线程池中的线程都有60s的超时机制,超过时间的线程会被回收。

使用场景:大量的耗时较少的任务

 

3ScheduleThreadPool类型

通过Executors.newScheduleThreadPool(int corePoolSize)创建。

这种线程池的核心线程数是固定的,非核心线程数量没有限制,并且非核心线程执行完任务会被立刻回收,非核心线程没有闲置的状态。

使用场景:定时任务和固定周期的重复任务

 

4SingleThreadExecutor类型

通过Executors.newSingleThreadExecutor()创建

SingleThreadExecutor内部只有唯一一个核心线程用来执行任务,其确保了所有的任务都在一个线程中执行,SingleThreadExecutor存在的意义在于统一所有的外界任务到一个线程中去执行,这样这些任务就不需要处理线程同步的问题。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章