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()); }
通过特殊的队列实现延迟或定时的方式
- Java 线程池原理解析(二)
- Java 线程池原理解析(一)
- Java 线程池原理解析(三)
- JAVA线程池原理详解二
- 线程池原理解析
- 线程池原理解析
- 线程池原理解析
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- 专题二 1001
- 207 Course Schedule
- 根据类Student的定义,创建五个该类的对象,输出每个学生的信息, 计算并输出这五个学生Java语言成绩的平均值,以及计算并输出他们Java语言成绩的最大值和最小值。
- Windows安装MySQL5.7
- AnyEvent::HTTP 实现异步请求
- Java 线程池原理解析(二)
- Location and Maps(位置和地图)——翻译自developer.android.com
- Hexo系列3:博客优化
- Mac忘记root密码
- leetcode——48——Rotate Image
- C++封装篇 (下)
- Android API Guides---Radio Buttons
- C++文件的存取
- 两段Js