Java 线程池原理解析(二)

来源:互联网 发布:淘宝发布宝贝产品规格 编辑:程序博客网 时间:2024/05/16 12:48

我们已经分析了两种通过线程来执行任务的策略,即把所有任务放在单个线程中串行执行,以及将每个任务放在各自的线程中执行。这两种方式都存在一些严格的限制:串行执行的问题在于其糟糕的响应性和吞吐量,而为每个任务分配一个线程的问题在于资源管理的复杂性

线程池简化了线程的管理工作,并且java.util.concurrent提供了一种灵活的线程池实现作为Executor框架的一部分。在Java中,任务执行的主要抽象不是Thread,而是Executor

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

它将任务的提交过程与执行过程解耦开来,并用Runnable来表示任务。

private static final int NTHREADS = 100;    private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);    public static void main(String[] args){        try {            ServerSocket socket = new ServerSocket(80);            while (true){                final Socket connection = socket.accept();                Runnable task = new Runnable() {                    @Override                    public void run() {                        handleReuqest(connection);                    }                };                exec.execute(task);            }        } catch (IOException e) {            e.printStackTrace();        }    }

线程池是指管理一组工作线程的资源池。
线程池是与工作队列(Work Queue)密切相关的,其中在工作队列中保存了所有等待队列的任务。
工作者线程(Worker Thread)的任务很简单:从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。
“在线程池中执行任务”通过重用现有的线程而不是创建新线程,可以处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。并且,当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行。
我们可以通过调用Executors中的静态工厂方法之一来创建一个线程池:

  • newFixedThreadPool:将创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化。
  • newCachedThreadPool:将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制。
  • newSingleThreadExecutor:这是一个单线程的Executor,它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程替代。newSingleThreadExecutor能确保依照任务在队列中的顺序来串行执行。
  • newScheduledThreadPool:创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。
    这样从“为每个任务分配一个线程”变为基于线程池的策略,将对应用程序的稳定性产生巨大的影响:Web服务器不会再在高负载情况下失败,BUT尽管服务器不会因为创建过多的线程而失败,但是在足够长的时间内,如果任务到达的速度总是超过任务执行的速度,那么服务器仍有可能耗尽内存。因为等待执行的Runnable队列将不断增长。在下一篇当中会讲到可以通过设置一个有界工作队列在Executor框架内部解决此问题。
    上面我们介绍了可以创建四种不同的线程池,那我们来read the fucking source code!跟进去看一看是如何创建的
    第一种:newFixedThreadPool
public class Executors {     * @param nThreads the number of threads in the pool     * @return the newly created thread pool     * @throws IllegalArgumentException if {@code nThreads <= 0}     */    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }}

这里面调用了ThreadPoolExecutor创建线程池,继续跟进去读:

public class ThreadPoolExecutor extends AbstractExecutorService {/**     * Creates a new {@code ThreadPoolExecutor} with the given initial     * parameters and default thread factory and rejected execution handler.     * It may be more convenient to use one of the {@link Executors} factory     * methods instead of this general purpose constructor.     *     * @param corePoolSize the number of threads to keep in the pool, even     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set     * @param maximumPoolSize the maximum number of threads to allow in the     *        pool     * @param keepAliveTime when the number of threads is greater than     *        the core, this is the maximum time that excess idle threads     *        will wait for new tasks before terminating.     * @param unit the time unit for the {@code keepAliveTime} argument     * @param workQueue the queue to use for holding tasks before they are     *        executed.  This queue will hold only the {@code Runnable}     *        tasks submitted by the {@code execute} method.     * @throws IllegalArgumentException if one of the following holds:<br>     *         {@code corePoolSize < 0}<br>     *         {@code keepAliveTime < 0}<br>     *         {@code maximumPoolSize <= 0}<br>     *         {@code maximumPoolSize < corePoolSize}     * @throws NullPointerException if {@code workQueue} is null     */    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }}

注释较长,里面主要解释了参数的意义。
corePoolSize:线程池中线程的数量,即使这些线程并没有执行任务,也仍然待在线程池当中。
maximumPoolSize:线程池中允许存在线程的最大数量
keepAliveTime:当任务请求而生成的线程数量大于corePoolSize时,后来某个执行task的线程执行完毕并随后处于空闲状态。那么某个线程的空闲状态超过了存活时间,那么将被标记为可回收的。当线程池大小超过了corePoolSize时,这个线程将被终止。
unit:定义时间的单位
workQueue:未被执行的任务都利用此队列进行排队。

我们可以看到在这个构造方法当中,默认调用

this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);

出现了两个新参数,Executors.defaultThreadFactory()defaultHandler
继续跟进:

public static ThreadFactory defaultThreadFactory() {        return new DefaultThreadFactory();    }
static class DefaultThreadFactory implements ThreadFactory {        private static final AtomicInteger poolNumber = new AtomicInteger(1);        private final ThreadGroup group;        private final AtomicInteger threadNumber = new AtomicInteger(1);        private final String namePrefix;        DefaultThreadFactory() {            SecurityManager s = System.getSecurityManager();            group = (s != null) ? s.getThreadGroup() :                                  Thread.currentThread().getThreadGroup();            namePrefix = "pool-" +                          poolNumber.getAndIncrement() +                         "-thread-";        }        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;        }    }

这样就通过工厂方法在线程池中创建统一的线程。

那么问题来了,handler干什么用的呢?!

/** * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}. *  * @since 1.5 * @author Doug Lea */public interface RejectedExecutionHandler {...}

这里的handler是用来处理不能被线程池所执行的任务,我认为的应该至少有一下两种情况:
* 第一种:超过corePoolSize但是小于maximumPoolSize,但是超出corePoolSize的这部分线程等待时间过长以至于超出了keepAliveTime直接死掉了,这是handler应该就会把task拒绝掉。
* 第二种:现在corePoolSize已经满了,同样maximumPoolSize也已经满了,这是如果还有任务请求的话,handler会直接把task拒绝掉。

第二种:newCachedThreadPool

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

显然这种线程池容量是无限大的,没有task请求的时候就把回线程池中收所有空闲的线程,有的时候就添加新的线程

第三种:newSingleThreadExecutor

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

单线程,没什么好说的。

第四种:newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {        return new ScheduledThreadPoolExecutor(corePoolSize);    }
public ScheduledThreadPoolExecutor(int corePoolSize) {        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,              new DelayedWorkQueue());    }

通过特殊的队列实现延迟或定时的方式

1 0
原创粉丝点击