Java线程池


Java的线程池存在于java.util.concurrent包中的Executor框架。Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。他们的关系为


图片来自第七城市网。

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

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

Excutors为真正的实现者。Executor作为顶级接口,ExecutorService接口继承了Executor接口,也是线程池ThreadExecutorPool线程池实现的接口。


建造线程池的方法有五种,分别建造不同的线程池:

newCachedThreadPool

newFixedThreadPool

newSingleThreadExecutor

newScheduledThreadPool

newSingleThreadScheduledExecutor

五种方法都会建造一个线程池,我们取其中一个解读一下:

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

可见,调用该方法最终返回了一个新建的ThreadPoolExecutor类型对象,也就是我们说的线程池。

继续追踪:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

ThreadPoolExecutor的构造函数,第一个参数是corePoolSize,核心线程数,核心线程在线程池中永远活跃。第二个参数maximunPoolSize,最大线程数,线程池可以运行的最大的线程数量。第三个参数keepAliveTime,非核心线程保持存活的时间。第四个参数unit,时间的单位。第五个参数workQueue,阻塞队列。

线程池中的核心线程始终保持活跃,当有任务来时,如果有核心线程空闲,立刻让其执行任务;如果没有核心线程空闲,即将任务加入工作队列。如果工作队列已满,又有任务过来了,那么开启一个新线程,执行任务,此时工作队列一直保持满的状态。开启的非核心线程执行任务结束后,如果在存活时间内没有接到任务,就会被关闭。当开启的线程数目达到最大线程数目,阻塞队列也已经排满,将会调用第七个参数defaultHandler,实行拒绝策略。

可见,上文的构造函数实际上调用了另一构造函数完成了构造。下面来看一看ThreadPoolExecutor的第一构造函数:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

由上可见,原生构造函数实际上有七个参数,除了上面提到的五个,还有ThreadFactory类型参数和RejectedExecutionHandler类型参数。第七个参数RejectedExecutionHandler上文也提到过了,它是线程池在线程达到最大线程数,且阻塞队列也达到满的状态时,调用的拒绝策略。

RejectedExecutionHandler:

public interface RejectedExecutionHandler {

    /**
     * Method that may be invoked by a {@link ThreadPoolExecutor} when
     * {@link ThreadPoolExecutor#execute execute} cannot accept a
     * task.  This may occur when no more threads or queue slots are
     * available because their bounds would be exceeded, or upon
     * shutdown of the Executor.
     *
     * <p>In the absence of other alternatives, the method may throw
     * an unchecked {@link RejectedExecutionException}, which will be
     * propagated to the caller of {@code execute}.
     *
     * @param r the runnable task requested to be executed
     * @param executor the executor attempting to execute this task
     * @throws RejectedExecutionException if there is no remedy
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

ThreadFactory:

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

很明显,它是用来创建线程的。

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
这是newCachedThreadPool函数另一个重载形式。我们也可以调用这种构造函数指定ThreadFactory,设置线程池中的线程属性。

线程提交到线程池,需要调用ExecutorService接口中的submit函数:

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

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

Future<?> submit(Runnable task);

线程池的submit函数有三种重载形式。三种形式的函数的返回值也不完全相同。

第一种,参数为Callable<T>类型,返回值为<T> Future<T>类型,均为可变参数。

第二种,参数为Runnable类型和T类型,返回值为<T> Future<T>类型。

第三种,参数为Runnable类型,返回值为Future<?>类型。

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
Callable和Runnable类似,Callable<T>接口中仅有一个call方法,Runnable接口中仅有一个run方法,它们都是开启的新线程运行的函数体。不过Callable<T>的cal方法是有返回值的,返回值的类型也为T。


返回值的类型为Future<T>:

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
Future<T>中有多种方法,给使用者用来控制提交的任务的运行。cancel方法用来取消任务,参数用来确定是否允许在线程运行时进行中断。isCancelled方法用来查看任务是否被取消。isDone方法用来查看任务是否已经完成。get方法接收任务运行结束后的返回结果,这是一个阻塞操作。第二个get方法设置了阻塞的时长和时间单位。

由于Runnable的run方法没有返回值,所以,在submit中把Runnable作为参数还想要返回值的话,需要自己设定一个T类型的result参数,任务运行结束后,将返回result的值作为结果。


我们使用Executors创建的线程池,其最大线程都为Integer.MAX_VALUE,所以为了安全起见,我们一般直接使用ThreadPoolExecutor来创建线程池。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值