并发编程笔记十一:线程池

十一、线程池

11.1、Executor框架

11.2、Executor接口

Executor接口代表了一个执行器,他可以按照不同的策略去执行提交过来的任务。

public interface Executor {
    void execute(Runnable command);
}

 

通过execute(Runnable command)方法,我们可以将需要执行的任务交给Executor来执行,它将任务的提交和执行给解耦开来了。

12.3、ExecutorService接口

ExecutorService接口是Executor的子接口,他扩展了Executor的方法:例如关闭线程池和提交带有返回值的任务。

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);
    <T> Future<T> submit(Runnable task, T result);
    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;
}

 

(1)void shutdown();

关闭当前执行器,执行已经提交的任务,但是不接受新任务。

(2)List<Runnable> shutdownNow();

向正在执行任务的线程发送中断信号,来尝试停止所有正在执行的任务,但不保证一定能立即终止,不再处理等待列表的任务,并将等待列表的任务全部返回。

(3)boolean isShutdown();

执行器是否已经关闭。

(4)boolean isTerminated();

如果执行器关闭后,所有任务都完成了,就返回true。

(5)<T> Future<T> submit(Callable<T> task);

提交一个带有返回值的callable任务。

(6)<T> Future<T> submit(Runnable task, T result);

提交一个Runnable任务,返回一个Future,在任务成功完成时调用Future的get()方法可以得到指定的结果。

(7)Future<?> submit(Runnable task);

提交一个Runnable任务,返回一个Future,在任务成功完成时调用Future的get()方法可以得到null。

12.4、ScheduledExecutorService接口

ScheduledExecutorService接口是ExecutorService的子接口,它扩展了ExecutorService的方法:例如

public interface ScheduledExecutorService extends ExecutorService {
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
                                                  long period, TimeUnit unit);
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
                                                     long delay, TimeUnit unit);
}

 

(1)public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

延迟指定时间后执行Runnable任务。

(2)public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

延迟指定时间后执行Callable任务。

(3)public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

创建并执行一个在给定初始延迟后首次启用的Runnable任务,后续操作具有给定的周期。也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。如果任务的任何一个执行遇到异常,则后续执行都会被取消。否则,只能通过执行程序的取消或终止方法来终止该任务。如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。

(4)public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

创建并执行一个在给定初始延迟后首次启用的Runnable任务,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。如果任务的任一执行遇到异常,就会取消后续执行。否则,只能通过执行程序的取消或终止方法来终止该任务。

12.5、ThreadPoolExecutor类

ThreadPoolExecutor类是ExecutorService的实现类(中间隔了一层AbstractExecutorService抽象类)

ThreadPoolExecutor类表示线程池对象,它的构造方法如下,其他几个都是重载的构造方法。

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

12.5.1、ThreadPoolExecutor类是构造方法参数解析:

(1)int corePoolSize:线程池的基本大小;

(2)int maximumPoolSize:线程池最大大小;

(3)long keepAliveTime:超过基本大小的线程在空闲时的存活时间;

(4)TimeUnit unit:keepAliveTime参数的时间单位;

(5)BlockingQueue<Runnable> workQueue:等待执行的任务队列,在当前执行任务数大于线程池最大大小值时,任务会放入workQueue队列中;

(6)ThreadFactory threadFactory:线程工厂

(7)RejectedExecutionHandler handler:线程池的拒接策略。

线程池初始化的时候并没有线程,之后每提交一个任务都会按照threadFactory的方式创建一个新的线程,直到线程池大小达到基本大小corePoolSize,之后即使没有任务提交,基本大小内的线程也不会被销毁。

如果任务的提交速度超过了任务的处理速度,那么线程池会继续增大,直到线程的最大大小maximumPoolSize,之后便不再增大线程池的大小。如果之后还有任务提交上来,那么这些任务会被放入到任务队列workQueue中,如果workQueue队列被放满了,还有任务再继续提交,那么会采用相应的拒绝策略handler去处理。

如果线程数量超过了线程池的基本大小corePoolSize,而线程在指定的keepAliveTime时间内处于空闲状态,那么这个线程会被销毁。

12.5.2、BlockingQueue<Runnable> workQueue任务队列

workQueue任务队列是一个阻塞队列,大致分为3类:

(1)无界队列

队列大小没有界限或者大小非常大的队列称为无界队列。使用无界队列作为任务队列的话,所有的等待任务都会被塞入队列中,好处是不会采取拒绝策略,所有任务都有机会执行,坏处是如果堆积的任务太多的话会导致内存溢出,抛出OutOfMemoryError错误。

(2)有界队列

队列大小是有限的队列称为有界队列。为了防止内存被使用完抛出错误,使用有界队列+拒绝策略是一种比较好的方式。

(3)同步移交队列

SynchronousQueue是一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。同步队列没有任何内部容量。

12.5.3、RejectedExecutionHandler handler拒绝策略

当阻塞队列被填满后,再提交的任务就会走拒绝策略。

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

 

在ThreadPoolExecutor类内部类中,JDK 提供了四种拒绝策略。

(1)AbortPolicy

直接抛出RejectedExecutionException异常。这是默认的拒绝策略。

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

 

(2)CallerRunsPolicy

直接在当前提交任务的线程以同步的方式执行任务,不添加进线程池。

    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        public CallerRunsPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

 

(3)DiscardPolicy

直接无视被拒绝的任务

    public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

 

(4)DiscardOldestPolicy

丢弃最旧的未处理的任务,然后加入当前任务

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

 

11.6、Executors工具类

我们在开发的时候,如果需要用到线程池。一般都是利用Executors工具类,来创建JDK给我们定义好的不同的执行策略的线程池对象。

阿里巴巴开发规范貌似不允许使用这个类,不知道为什么。

(1)public static ExecutorService newFixedThreadPool(int nThreads)

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

 

创建固定线程数量的线程池,队列使用的是无界队列。

(2)public static ExecutorService newCachedThreadPool()

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

 

创建一个根据需要自动扩容的线程池对象,如果线程超过60秒空闲,那么将会被回收。

(3)public static ExecutorService newSingleThreadExecutor()

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

 

创建一个仅有一个线程的线程池对象,队列使用的是无界队列。

(4)public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

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

 

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

11.7、线程池使用的注意事项

(1)线程池的线程数量计算

对于cpu密集型的程序,一般将线程数设置成:处理器数量+1。

对于其他密集型的程序,一般将其他资源充分利用即可。

(2)区分不同的线程池

不同的任务最好分散在不同的线程池中去执行。

(3)ThreadLocal的使用

对于ThreadLock变量要在任务执行过程中创建,在任务执行结束前销毁。不然这个值会被带入下一个任务中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值