JAVA 线程池
、ThreadPoolExecutor的使用
1.ThreadPoolExecutor的完整构造方法是:
ThreadPoolExecutor ( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue< Runnable > workQueue, RejectedExecutionHandler hander)
2.Executors中的几个工厂方法
ThreadPoolExecutor是Executors类的底层实现
①newFixedThreadPool ( int nThreads)——固定大小线程池
[java]
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到corePoolSize和maximunPoolSize的大小是一样的(其实使用易做图Queue的话maximumPoolSize参数是没有意义的),keepAliveTime和unit的设置表明该实现不想keepAlive,最后的BlockingQueue选择了LinkedBlockingQueue,该Queue有一个特点就是它是易做图的;
②newSingleThreadExecutor()——单线程
[java]
public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
③newCachedThreadPool()——易做图线程池,可以进行自动线程回收
[java]
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
在BlockingQueue的选择上使用SynchronousQueue,在该Queue中,每个插入操作必须等待另一个线程的对应移除操作。比如,我先添加一个元素,接下来如果想继续添加则会阻塞,直到另一个线程取走一个元素,反之亦然(也就是缓冲区为1的生产者消费者模式)。
3.corePoolSize、workQueue、maximumPoolSize的优先级
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
二、BlockingQueue<Runnable> workQueue
1.BlockingQueue的作用
所有的BlockingQueue都可以用于传输和保持提交的任务,可以使用此队列对池大小进行交互。
如果运行的线程小于corePoolSize,则Executor始终首选添加新的线程,而不进行排队,也就是当当前运行的线程小于corePoolSize时,则任务根本不会存放,添加到Queue中,而是直接创建一个新的thread;
如果运行的线程等于或多余corePoolSize,则Executor首选将请求加入Queue,而不是添加新的thread;
如果无法将请求加入Queue,则创建新线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。
2.排队的三种策略
①直接提交,工作队列的默认选项是SynchronousQueue,它将任务交给线程而不保持它们,如果不存在可用于立即执行的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求易做图maximumPoolSize以避免拒绝新提交的任务,当命令以超过队列所能处理的平均连续到达时,此策略允许易做图线程具有增长的可能性。
②易做图队列,使用易做图队列如LinkedBlockingQueue将导致在所有corePoolSize线程都忙时新任务在队列中等待,这样创建的线程数就不会超过corePoolSize,因此maximumPoolSize的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用易做图队列。
③有界队列,当使用有限的maximumPoolSize时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的
补充:软件开发 , Java ,