Java线程池的使用(ThreadPoolExecutor简介)

#什么是线程池?
线程池是一种多线程处理形式,java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。

如何使用线程池#

ThreadPoolExecutor简介

首先看ThreadPoolExecutor的继承关系:ThreadPoolExecutor继承AbstractExecutorService,其中AbstractExecutorService又实现了ExecutorService接口,ExecutorService又继承了Executor接口。

Executor接口里面只有一个回调方法execute(Runnable command):

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);
}


接口ExecutorService,给出一系列方法,例如shutdown,可以实时关闭Excutor执行等。另外还可以通过Future类来跟随判断当前执行任务的状态。还有submit方法用来提交执行的新任务,并且Future通过ExcutorService里面的submit方法来返回得到。


抽象类AbstractExecutorService实现了ExecutorService里面一些功能。比如submit():

    /**
     * Returns a {@code RunnableFuture} for the given runnable and default
     * value.
     *
     * @param runnable the runnable task being wrapped
     * @param value the default value for the returned future
     * @param  the type of the given value
     * @return a {@code RunnableFuture} which, when run, will run the
     * underlying runnable and which, as a {@code Future}, will yield
     * the given value as its result and provide for cancellation of
     * the underlying task
     * @since 1.6
     */
    protected  RunnableFuture newTaskFor(Runnable runnable, T value) {
        return new FutureTask(runnable, value);
    }

    /**
     * Returns a {@code RunnableFuture} for the given callable task.
     *
     * @param callable the callable task being wrapped
     * @param  the type of the callable's result
     * @return a {@code RunnableFuture} which, when run, will call the
     * underlying callable and which, as a {@code Future}, will yield
     * the callable's result as its result and provide for
     * cancellation of the underlying task
     * @since 1.6
     */
    protected  RunnableFuture newTaskFor(Callable callable) {
        return new FutureTask(callable);
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public  Future submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public  Future submit(Callable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

可以看出来上面的返回值是一个FutureTask。FutureTask是对Future接口的间接实现。中间有个Runnable接口。在这里就不在多说知道它的功能即可(用来追踪Task任务的执行状态)。


最后的线程维护各种东西都是在ThreadPoolExecutor这个类里面进行的。
下面是ThreadPoolExecutor最核心的构造方法:

参数名 作用
corePoolSize 核心线程池大小
maximumPoolSize 最大线程池大小
keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
TimeUnit keepAliveTime时间单位
workQueue 阻塞任务队列
threadFactory 新建线程工厂
RejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭


线程池的几种常见的创建的方式: Executors提供的线程池配置方案

  • 创建一个可缓存线程池:如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。此程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
ExecutorService es = Executors.newCachedThreadPool();
  • 创建固定数量线程的线程池:可控制线程最大并发数,超出的线程会在队列中等待。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
ExecutorService es = Executors.newFixedThreadPool(2);
  • 创建单线程的线程池:它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
ExecutorService es = Executors.newSingleThreadExecutor();
  • 创建定时线程:支持定时及周期性任务执行。
ScheduledExecutorService es = Executors.newScheduledThreadPool(2);

通过ThreadPoolExecutor创建线程池##

以上Executors利用工厂模式向我们提供了4种线程池实现方式,但是并不推荐使用,原因是使用Executors创建线程池不会传入这个参数而使用默认值所以我们常常忽略这一参数,而且默认使用的参数会导致资源浪费,不可取。最好通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

  • 构造一个缓冲功能的线程池,配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一个无容量的阻塞队列 SynchronousQueue,因此任务提交之后,将会创建新的线程执行;线程空闲超过60s将会销毁 :
ThreadPoolExecutor cachedThreadPool = new ThreadPoolExecutor(
                0,
                Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue(),
                new ThreadPoolExecutor.AbortPolicy());
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
            }
        });
        cachedThreadPool.shutdown();
  • 构造一个固定线程数目(2)的线程池,配置的corePoolSize与maximumPoolSize大小相同,同时使用了一个无界LinkedBlockingQueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由RejectedExecutionHandler处理:
ThreadPoolExecutor singleThreadPool = new ThreadPoolExecutor(
            2,
            2,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue(),
            new ThreadPoolExecutor.AbortPolicy());
    singleThreadPool.execute(new

    Runnable() {
        @Override
        public void run () {
        }
    });
    singleThreadPool.shutdown();
  • 构造一个只支持一个线程的线程池,配置corePoolSize=maximumPoolSize=1,无界阻塞队列LinkedBlockingQueue;保证任务由一个线程串行执行 :
ThreadPoolExecutor singleThreadPool = new ThreadPoolExecutor(
                1,
                1,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue(),
                new ThreadPoolExecutor.AbortPolicy());
        singleThreadPool.execute(new Runnable() {
            @Override
            public void run() {
            }
        });
        singleThreadPool.shutdown();
  • 构造有定时功能的线程池,配置corePoolSize,无界延迟阻塞队列DelayedWorkQueue;有意思的是:maximumPoolSize=Integer.MAX_VALUE,由于DelayedWorkQueue是无界队列,所以这个值是没有意义的 :
        // 延时任务
        executorService1.schedule(new TimerTask() {
            @Override
            public void run() {
                finish();
                startActivity(new Intent(WelcomeUI.this, TabUI.class));
            }
        },1, TimeUnit.SECONDS);
        executorService1.shutdown();

        ScheduledExecutorService executorService2 = new ScheduledThreadPoolExecutor(1,new ThreadPoolExecutor.AbortPolicy());
        // 从0秒开始每隔2秒执行一次
        executorService2.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                finish();
                startActivity(new Intent(WelcomeUI.this, TabUI.class));
            }
        },0, 2,TimeUnit.SECONDS);
        executorService2.shutdown();

        ScheduledExecutorService executorService3 = new ScheduledThreadPoolExecutor(1,new ThreadPoolExecutor.AbortPolicy());
        // 延迟5秒间隔3秒执行(第一次间隔8秒,之后3秒执行一次,但是间隔3秒后会检测上一个任务是否执行完毕如果没有会等待执行完毕后执行)
        executorService3.scheduleWithFixedDelay (new TimerTask() {
            @Override
            public void run() {
                finish();
                startActivity(new Intent(WelcomeUI.this, TabUI.class));
            }
        },5, 3,TimeUnit.SECONDS);
        executorService3.shutdown();

你可能感兴趣的:(Java线程池的使用(ThreadPoolExecutor简介))